Merge "Add functions to parcel nullable surface controls"
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 3467898..67a77f6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD1(listServices, Vector<String16>(int));
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
MOCK_METHOD1(isDeclared, bool(const String16&));
+ MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 2c3efe5..99cb93a 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -141,7 +141,7 @@
}
}
- PipeRelay relay(out);
+ PipeRelay relay(out, err, interfaceName, instanceName);
if (relay.initCheck() != OK) {
std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 820679f..4e97636 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -23,7 +23,6 @@
#include <atomic>
-#include <android-base/logging.h>
#include <utils/Thread.h>
namespace android {
@@ -31,8 +30,15 @@
static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
+static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
+ auto dot = interfaceName.rfind(".");
+ if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
+ return "RelayThread_" + interfaceName + "_" + instanceName;
+}
+
struct PipeRelay::RelayThread : public Thread {
- explicit RelayThread(int fd, std::ostream &os);
+ explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &fqName);
bool threadLoop() override;
void setFinished();
@@ -40,6 +46,7 @@
private:
int mFd;
std::ostream &mOutStream;
+ NullableOStream<std::ostream> mErrStream;
// If we were to use requestExit() and exitPending() instead, threadLoop()
// may not run at all by the time ~PipeRelay is called (i.e. debug() has
@@ -47,13 +54,17 @@
// read() are executed until data are drained.
std::atomic_bool mFinished;
+ std::string mFqName;
+
DISALLOW_COPY_AND_ASSIGN(RelayThread);
};
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
- : mFd(fd), mOutStream(os), mFinished(false) {}
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
+ const NullableOStream<std::ostream> &err,
+ const std::string &fqName)
+ : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
bool PipeRelay::RelayThread::threadLoop() {
char buffer[1024];
@@ -66,13 +77,14 @@
int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
if (res < 0) {
- PLOG(INFO) << "select() failed";
+ mErrStream << "debug " << mFqName << ": select() failed";
return false;
}
if (res == 0 || !FD_ISSET(mFd, &set)) {
if (mFinished) {
- LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
+ mErrStream << "debug " << mFqName
+ << ": timeout reading from pipe, output may be truncated.";
return false;
}
// timeout, but debug() has not returned, so wait for HAL to finish.
@@ -83,7 +95,7 @@
ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
if (n < 0) {
- PLOG(ERROR) << "read() failed";
+ mErrStream << "debug " << mFqName << ": read() failed";
}
if (n <= 0) {
@@ -101,8 +113,9 @@
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::PipeRelay(std::ostream &os)
- : mInitCheck(NO_INIT) {
+PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &interfaceName, const std::string &instanceName)
+ : mInitCheck(NO_INIT) {
int res = pipe(mFds);
if (res < 0) {
@@ -110,8 +123,8 @@
return;
}
- mThread = new RelayThread(mFds[0], os);
- mInitCheck = mThread->run("RelayThread");
+ mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
+ mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
}
void PipeRelay::CloseFd(int *fd) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 835016041..bd994b4 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -21,6 +21,8 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include "NullableOStream.h"
+
namespace android {
namespace lshal {
@@ -28,7 +30,10 @@
* written to the "write"-end of the pair to the specified output stream "os".
*/
struct PipeRelay {
- explicit PipeRelay(std::ostream &os);
+ explicit PipeRelay(std::ostream& os,
+ const NullableOStream<std::ostream>& err,
+ const std::string& interfaceName,
+ const std::string& instanceName);
~PipeRelay();
status_t initCheck() const;
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index e80c321..c8355e2 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -37,6 +37,27 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
+struct ManifestWithDescription {
+ std::shared_ptr<const vintf::HalManifest> manifest;
+ const char* description;
+};
+// func true -> stop search and forEachManifest will return true
+static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
+ for (const ManifestWithDescription& mwd : {
+ ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+ ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
+ }) {
+ if (mwd.manifest == nullptr) {
+ LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
+ // note, we explicitly do not retry here, so that we can detect VINTF
+ // or other bugs (b/151696835)
+ continue;
+ }
+ if (func(mwd)) return true;
+ }
+ return false;
+}
+
static bool isVintfDeclared(const std::string& name) {
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
@@ -49,31 +70,41 @@
const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
const std::string instance = name.substr(firstSlash+1);
- struct ManifestWithDescription {
- std::shared_ptr<const vintf::HalManifest> manifest;
- const char* description;
- };
- for (const ManifestWithDescription& mwd : {
- ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
- ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
- }) {
- if (mwd.manifest == nullptr) {
- LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
- // note, we explicitly do not retry here, so that we can detect VINTF
- // or other bugs (b/151696835)
- continue;
- }
+ bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
return true;
}
+ return false; // continue
+ });
+
+ if (!found) {
+ // Although it is tested, explicitly rebuilding qualified name, in case it
+ // becomes something unexpected.
+ LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+ << " in the VINTF manifest.";
}
- // Although it is tested, explicitly rebuilding qualified name, in case it
- // becomes something unexpected.
- LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
- << " in the VINTF manifest.";
- return false;
+ return found;
+}
+
+static std::vector<std::string> getVintfInstances(const std::string& interface) {
+ size_t lastDot = interface.rfind('.');
+ if (lastDot == std::string::npos) {
+ LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+ return {};
+ }
+ const std::string package = interface.substr(0, lastDot);
+ const std::string iface = interface.substr(lastDot+1);
+
+ std::vector<std::string> ret;
+ (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+ auto instances = mwd.manifest->getAidlInstances(package, iface);
+ ret.insert(ret.end(), instances.begin(), instances.end());
+ return false; // continue
+ });
+
+ return ret;
}
static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
@@ -331,6 +362,30 @@
return Status::ok();
}
+binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> allInstances;
+#ifndef VENDORSERVICEMANAGER
+ allInstances = getVintfInstances(interface);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : allInstances) {
+ // TODO(b/169275998): allow checking policy only once for the interface
+ if (mAccess->canFind(ctx, interface + "/" + instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && allInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index a2fc5a8..9f43eb4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,6 +44,7 @@
const sp<IServiceCallback>& callback) override;
binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
index cc4c842..32bcc83 100644
--- a/cmds/surfacereplayer/OWNERS
+++ b/cmds/surfacereplayer/OWNERS
@@ -1,2 +1 @@
-mathias@google.com
-racarr@google.com
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index cf78102..ccf4dc8 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,7 +38,6 @@
<!-- basic system services -->
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
- <feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/include/input/Input.h b/include/input/Input.h
index 258adae..7d81fed 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -141,14 +141,6 @@
#define MAX_CONTROLLER_LEDS 4
/*
- * SystemUiVisibility constants from View.
- */
-enum {
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9aa82d9..6d728dc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -74,6 +74,7 @@
Vector<String16> listServices(int dumpsysPriority) override;
sp<IBinder> waitForService(const String16& name16) override;
bool isDeclared(const String16& name) override;
+ Vector<String16> getDeclaredInstances(const String16& interface) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -373,4 +374,18 @@
return declared;
}
+Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+ std::vector<std::string> out;
+ if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
} // namespace android
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ff15460..2b1e492 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -99,6 +99,14 @@
boolean isDeclared(@utf8InCpp String name);
/**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ */
+ @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 1d520c1..3c5ccc1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -96,6 +96,11 @@
* service.
*/
virtual bool isDeclared(const String16& name) = 0;
+
+ /**
+ * Get all instances of a service as declared in the VINTF manifest
+ */
+ virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
};
sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 51fd84c..ca03397 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -73,12 +73,11 @@
AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
AIBinder::~AIBinder() {}
-bool AIBinder::associateClass(const AIBinder_Class* clazz) {
- if (clazz == nullptr) return false;
+std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
+ const String8& newDescriptor, bool set) {
+ std::lock_guard<std::mutex> lock(mClazzMutex);
if (mClazz == clazz) return true;
- String8 newDescriptor(clazz->getInterfaceDescriptor());
-
if (mClazz != nullptr) {
String8 currentDescriptor(mClazz->getInterfaceDescriptor());
if (newDescriptor == currentDescriptor) {
@@ -97,6 +96,22 @@
return false;
}
+ if (set) {
+ // if this is a local object, it's not one known to libbinder_ndk
+ mClazz = clazz;
+ }
+
+ return {};
+}
+
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
+ if (clazz == nullptr) return false;
+
+ String8 newDescriptor(clazz->getInterfaceDescriptor());
+
+ auto result = associateClassInternal(clazz, newDescriptor, false);
+ if (result.has_value()) return *result;
+
CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor
String8 descriptor(getBinder()->getInterfaceDescriptor());
@@ -112,10 +127,7 @@
return false;
}
- // if this is a local object, it's not one known to libbinder_ndk
- mClazz = clazz;
-
- return true;
+ return associateClassInternal(clazz, newDescriptor, true).value_or(true);
}
ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 902fe79..bf27046 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -22,6 +22,7 @@
#include <atomic>
#include <mutex>
+#include <optional>
#include <vector>
#include <binder/Binder.h>
@@ -52,10 +53,14 @@
}
private:
+ std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
+ const ::android::String8& newDescriptor, bool set);
+
// AIBinder instance is instance of this class for a local object. In order to transact on a
// remote object, this also must be set for simplicity (although right now, only the
// interfaceDescriptor from it is used).
const AIBinder_Class* mClazz;
+ std::mutex mClazzMutex;
};
// This is a local AIBinder object with a known class.
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index f59bb75..439b019 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -199,6 +199,9 @@
public:
/**
* Takes ownership of a.
+ *
+ * WARNING: this constructor is only expected to be used when reading a
+ * status value. Use `ScopedAStatus::ok()` instead.
*/
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 4fd59a2..a8ae441 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -23,7 +23,8 @@
using ::android::binder::Status;
AStatus* AStatus_newOk() {
- return new AStatus();
+ static AStatus status = AStatus();
+ return &status;
}
AStatus* AStatus_fromExceptionCode(binder_exception_t exception) {
@@ -78,7 +79,9 @@
}
void AStatus_delete(AStatus* status) {
- delete status;
+ if (status != AStatus_newOk()) {
+ delete status;
+ }
}
binder_status_t PruneStatusT(status_t status) {
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 0234820..dc8270e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -28,11 +28,8 @@
rust_bindgen {
name: "libbinder_ndk_bindgen",
crate_name: "binder_ndk_bindgen",
- wrapper_src: "sys/BinderBindings.h",
+ wrapper_src: "sys/BinderBindings.hpp",
source_stem: "bindings",
- cflags: [
- "-x c++",
- ],
bindgen_flags: [
// Unfortunately the only way to specify the rust_non_exhaustive enum
// style for a type is to make it the default
diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.hpp
similarity index 100%
rename from libs/binder/rust/sys/BinderBindings.h
rename to libs/binder/rust/sys/BinderBindings.hpp
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6964324..4ecbe53 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -61,4 +61,16 @@
return mNameToService.find(name) != mNameToService.end();
}
+Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+ Vector<String16> out;
+ const String16 prefix = name + String16("/");
+ for (const auto& [registeredName, service] : mNameToService) {
+ (void) service;
+ if (registeredName.startsWith(prefix)) {
+ out.add(String16(registeredName.string() + prefix.size()));
+ }
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index a6b6b84..4ef47fb 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -46,6 +46,8 @@
bool isDeclared(const String16& name) override;
+ Vector<String16> getDeclaredInstances(const String16& iface) override;
+
private:
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 119b3e0..55c5de9 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -466,7 +466,8 @@
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string developerOptIn, const int rulesFd,
+ const std::string developerOptIn,
+ const std::vector<std::string> eglFeatures, const int rulesFd,
const long rulesOffset, const long rulesLength) {
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
@@ -475,6 +476,8 @@
return;
}
+ mAngleEglFeatures = std::move(eglFeatures);
+
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
ALOGV("setting ANGLE app name to '%s'", appName.c_str());
@@ -520,6 +523,10 @@
return mAngleAppName;
}
+const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
+ return mAngleEglFeatures;
+}
+
const std::string& GraphicsEnv::getLayerPaths() {
return mLayerPaths;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 22a2332..900fc49 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,12 +97,15 @@
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const int rulesFd, const long rulesOffset, const long rulesLength);
+ const std::vector<std::string> eglFeatures, const int rulesFd,
+ const long rulesOffset, const long rulesLength);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
std::string& getAngleAppName();
+ const std::vector<std::string>& getAngleEglFeatures();
+
/*
* Apis for debug layer
*/
@@ -154,6 +157,8 @@
std::string mAngleAppName;
// ANGLE developer opt in status.
std::string mAngleDeveloperOptIn;
+ // ANGLE EGL features;
+ std::vector<std::string> mAngleEglFeatures;
// ANGLE rules.
std::vector<char> mRulesBuffer;
// Use ANGLE flag.
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56591bd..ba72f9b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "BLASTBufferQueue"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+// #define LOG_NDEBUG 0
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -29,8 +30,20 @@
using namespace std::chrono_literals;
+namespace {
+inline const char* toString(bool b) {
+ return b ? "true" : "false";
+}
+} // namespace
+
namespace android {
+// Macros to include adapter info in log messages
+#define BQA_LOGV(x, ...) \
+ ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+#define BQA_LOGE(x, ...) \
+ ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+
void BLASTBufferItemConsumer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mPreviouslyConnected = mCurrentlyConnected;
@@ -93,9 +106,10 @@
if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
}
-BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering)
- : mSurfaceControl(surface),
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+ int width, int height, bool enableTripleBuffering)
+ : mName(name),
+ mSurfaceControl(surface),
mWidth(width),
mHeight(height),
mNextTransaction(nullptr) {
@@ -110,9 +124,9 @@
mBufferItemConsumer =
new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
static int32_t id = 0;
- auto name = std::string("BLAST Consumer") + std::to_string(id);
+ auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
id++;
- mBufferItemConsumer->setName(String8(name.c_str()));
+ mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
@@ -127,7 +141,7 @@
mPendingReleaseItem.releaseFence = nullptr;
}
-void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) {
std::unique_lock _lock{mMutex};
mSurfaceControl = surface;
@@ -153,6 +167,8 @@
std::unique_lock _lock{mMutex};
ATRACE_CALL();
+ BQA_LOGV("transactionCallback");
+
if (!stats.empty()) {
mTransformHint = stats[0].transformHint;
mBufferItemConsumer->setTransformHint(mTransformHint);
@@ -195,6 +211,8 @@
void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
ATRACE_CALL();
+ BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
+
if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
return;
}
@@ -246,15 +264,22 @@
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
+ t->setFrame(mSurfaceControl,
+ {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
t->setDesiredPresentTime(bufferItem.mTimestamp);
+ t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (applyTransaction) {
t->apply();
}
+
+ BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+ " applyTransaction=%s mTimestamp=%" PRId64,
+ mWidth, mHeight, bufferItem.mFrameNumber, toString(applyTransaction),
+ bufferItem.mTimestamp);
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -268,7 +293,10 @@
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- if (mNextTransaction != nullptr) {
+ const bool nextTransactionSet = mNextTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable nextTransactionSet=%s", toString(nextTransactionSet));
+
+ if (nextTransactionSet) {
while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
mCallbackCV.wait(_lock);
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6ff4a3d..150a36d 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -45,7 +45,7 @@
SAFE_PARCEL(output.write, crop_legacy);
SAFE_PARCEL(output.writeStrongBinder, barrierHandle_legacy);
SAFE_PARCEL(output.writeStrongBinder, reparentHandle);
- SAFE_PARCEL(output.writeUint64, frameNumber_legacy);
+ SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
SAFE_PARCEL(output.writeInt32, overrideScalingMode);
SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(barrierGbp_legacy));
SAFE_PARCEL(output.writeStrongBinder, relativeLayerHandle);
@@ -108,6 +108,7 @@
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
+ SAFE_PARCEL(output.writeUint64, frameNumber);
return NO_ERROR;
}
@@ -134,7 +135,7 @@
SAFE_PARCEL(input.read, crop_legacy);
SAFE_PARCEL(input.readNullableStrongBinder, &barrierHandle_legacy);
SAFE_PARCEL(input.readNullableStrongBinder, &reparentHandle);
- SAFE_PARCEL(input.readUint64, &frameNumber_legacy);
+ SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
SAFE_PARCEL(input.readInt32, &overrideScalingMode);
sp<IBinder> tmpBinder;
@@ -213,6 +214,7 @@
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
+ SAFE_PARCEL(input.readUint64, &frameNumber);
return NO_ERROR;
}
@@ -340,7 +342,7 @@
what |= eDeferTransaction_legacy;
barrierHandle_legacy = other.barrierHandle_legacy;
barrierGbp_legacy = other.barrierGbp_legacy;
- frameNumber_legacy = other.frameNumber_legacy;
+ barrierFrameNumber = other.barrierFrameNumber;
}
if (other.what & eOverrideScalingModeChanged) {
what |= eOverrideScalingModeChanged;
@@ -456,6 +458,10 @@
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
}
+ if (other.what & eFrameNumberChanged) {
+ what |= eFrameNumberChanged;
+ frameNumber = other.frameNumber;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0264e17..07a66c4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1017,7 +1017,7 @@
}
s->what |= layer_state_t::eDeferTransaction_legacy;
s->barrierHandle_legacy = handle;
- s->frameNumber_legacy = frameNumber;
+ s->barrierFrameNumber = frameNumber;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1034,7 +1034,7 @@
}
s->what |= layer_state_t::eDeferTransaction_legacy;
s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
- s->frameNumber_legacy = frameNumber;
+ s->barrierFrameNumber = frameNumber;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1324,6 +1324,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
+ const sp<SurfaceControl>& sc, uint64_t frameNumber) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eFrameNumberChanged;
+ s->frameNumber = frameNumber;
+
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2320771..35c656a 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -66,8 +66,8 @@
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
- BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering = true);
+ BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+ int height, bool enableTripleBuffering = true);
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
@@ -81,7 +81,7 @@
const std::vector<SurfaceControlStats>& stats);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
- void update(const sp<SurfaceControl>& surface, int width, int height);
+ void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
virtual ~BLASTBufferQueue() = default;
@@ -95,6 +95,7 @@
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item);
+ std::string mName;
sp<SurfaceControl> mSurfaceControl;
std::mutex mMutex;
@@ -106,17 +107,18 @@
int32_t mNumFrameAvailable GUARDED_BY(mMutex);
int32_t mNumAcquired GUARDED_BY(mMutex);
-
struct PendingReleaseItem {
BufferItem item;
sp<Fence> releaseFence;
};
std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+ // Keep a reference to the currently presented buffer so we can release it when the next buffer
+ // is ready to be presented.
PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
- int mWidth GUARDED_BY(mMutex);
- int mHeight GUARDED_BY(mMutex);
+ uint32_t mWidth GUARDED_BY(mMutex);
+ uint32_t mHeight GUARDED_BY(mMutex);
uint32_t mTransformHint GUARDED_BY(mMutex);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 7a9bb12..96ff61b 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -125,6 +125,7 @@
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
+ eFrameNumberChanged = 0x400'00000000,
};
layer_state_t()
@@ -142,7 +143,7 @@
crop_legacy(Rect::INVALID_RECT),
cornerRadius(0.0f),
backgroundBlurRadius(0),
- frameNumber_legacy(0),
+ barrierFrameNumber(0),
overrideScalingMode(-1),
transform(0),
transformToDisplayInverse(false),
@@ -159,7 +160,8 @@
frameRateSelectionPriority(-1),
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
- fixedTransformHint(ui::Transform::ROT_INVALID) {
+ fixedTransformHint(ui::Transform::ROT_INVALID),
+ frameNumber(0) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -195,7 +197,7 @@
uint32_t backgroundBlurRadius;
sp<IBinder> barrierHandle_legacy;
sp<IBinder> reparentHandle;
- uint64_t frameNumber_legacy;
+ uint64_t barrierFrameNumber;
int32_t overrideScalingMode;
sp<IGraphicBufferProducer> barrierGbp_legacy;
@@ -259,6 +261,10 @@
// a buffer of a different size. -1 means the transform hint is not set,
// otherwise the value will be a valid ui::Rotation.
ui::Transform::RotationFlags fixedTransformHint;
+
+ // Used by BlastBufferQueue to forward the framenumber generated by the
+ // graphics producer.
+ uint64_t frameNumber;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index bbe3893..d86ab01 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -493,6 +493,8 @@
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+ // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
+ Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index da0d5f8..35fff0a 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -44,7 +44,7 @@
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+ mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 16afc68..365e788 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -51,7 +51,15 @@
}
int slot = item.mSlot;
+ *outQueueEmpty = false;
if (item.mFence->isValid()) {
+ // If fence is not signaled, that means frame is not ready and
+ // outQueueEmpty is set to true. By the time the fence is signaled,
+ // there may be a new buffer queued. This is a proper detection for an
+ // empty queue and it is needed to avoid infinite loop in
+ // ASurfaceTexture_dequeueBuffer (see b/159921224).
+ *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled;
+
// Wait on the producer fence for the buffer to be ready.
err = fenceWait(item.mFence->get(), fencePassThroughHandle);
if (err != OK) {
@@ -112,7 +120,6 @@
st.mCurrentFrameNumber = item.mFrameNumber;
st.computeCurrentTransformMatrixLocked();
- *outQueueEmpty = false;
*outDataspace = item.mDataSpace;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 1ccbff1..eb967ce 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -77,6 +77,7 @@
srcs: [
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
+ "skia/filters/BlurFilter.cpp",
],
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cb752b0..afedd48 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -19,24 +19,27 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <cmath>
+#include "SkiaGLRenderEngine.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
+#include <GrContextOptions.h>
+#include <SkCanvas.h>
+#include <SkColorSpace.h>
+#include <SkImage.h>
+#include <SkImageFilters.h>
+#include <SkShadowUtils.h>
+#include <SkSurface.h>
+#include <gl/GrGLInterface.h>
#include <sync/sync.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
+
+#include <cmath>
+
#include "../gl/GLExtensions.h"
-#include "SkiaGLRenderEngine.h"
-
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
-
-#include <SkCanvas.h>
-#include <SkImage.h>
-#include <SkShadowUtils.h>
-#include <SkSurface.h>
+#include "filters/BlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -128,6 +131,47 @@
return err;
}
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+static sk_sp<SkColorSpace> toColorSpace(ui::Dataspace dataspace) {
+ skcms_Matrix3x3 gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkNamedGamut::kRec2020;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkNamedGamut::kDisplayP3;
+ break;
+ default:
+ ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ default:
+ ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ }
+}
+
std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
const RenderEngineCreationArgs& args) {
// initialize EGL for the default display
@@ -194,7 +238,7 @@
// initialize the renderer while GL is current
std::unique_ptr<SkiaGLRenderEngine> engine =
- std::make_unique<SkiaGLRenderEngine>(display, config, ctxt, placeholder,
+ std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
protectedContext, protectedPlaceholder);
ALOGI("OpenGL ES informations:");
@@ -247,15 +291,16 @@
return config;
}
-SkiaGLRenderEngine::SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
- EGLSurface placeholder, EGLContext protectedContext,
- EGLSurface protectedPlaceholder)
+SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+ EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+ EGLContext protectedContext, EGLSurface protectedPlaceholder)
: mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
- mProtectedPlaceholderSurface(protectedPlaceholder) {
+ mProtectedPlaceholderSurface(protectedPlaceholder),
+ mUseColorManagement(args.useColorManagement) {
// Suppress unused field warnings for things we definitely will need/use
// These EGL fields will all be needed for toggling between protected & unprotected contexts
// Or we need different RE instances for that
@@ -273,6 +318,10 @@
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+
+ if (args.supportsBackgroundBlur) {
+ mBlurFilter = new BlurFilter();
+ }
}
base::unique_fd SkiaGLRenderEngine::flush() {
@@ -379,7 +428,10 @@
if (!surface) {
surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
- SkColorSpace::MakeSRGB(), nullptr);
+ mUseColorManagement
+ ? toColorSpace(display.outputDataspace)
+ : SkColorSpace::MakeSRGB(),
+ nullptr);
if (useFramebufferCache && surface) {
ALOGD("Adding to cache");
mSurfaceCache.insert({buffer->getId(), surface});
@@ -408,6 +460,11 @@
const auto& bounds = layer->geometry.boundaries;
const auto dest = getSkRect(bounds);
+ if (layer->backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+ }
+
if (layer->source.buffer.buffer) {
ATRACE_NAME("DrawImage");
const auto& item = layer->source.buffer;
@@ -419,7 +476,11 @@
image = SkImage::MakeFromAHardwareBuffer(item.buffer->toAHardwareBuffer(),
item.usePremultipliedAlpha
? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType);
+ : kUnpremul_SkAlphaType,
+ mUseColorManagement
+ ? toColorSpace(
+ layer->sourceDataspace)
+ : SkColorSpace::MakeSRGB());
mImageCache.insert({item.buffer->getId(), image});
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 3da7f25..ed4ba11 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -17,17 +17,21 @@
#ifndef SF_SKIAGLRENDERENGINE_H_
#define SF_SKIAGLRENDERENGINE_H_
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/RenderEngine.h>
#include <sys/types.h>
+
#include <mutex>
#include <unordered_map>
-#include <android-base/thread_annotations.h>
-#include <renderengine/RenderEngine.h>
-
-#include <GrDirectContext.h>
-#include <SkSurface.h>
-
+#include "EGL/egl.h"
#include "SkiaRenderEngine.h"
+#include "filters/BlurFilter.h"
namespace android {
namespace renderengine {
@@ -36,8 +40,8 @@
class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
public:
static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
- EGLSurface placeholder, EGLContext protectedContext,
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+ EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override{};
@@ -78,6 +82,9 @@
EGLSurface mPlaceholderSurface;
EGLContext mProtectedEGLContext;
EGLSurface mProtectedPlaceholderSurface;
+ BlurFilter* mBlurFilter = nullptr;
+
+ const bool mUseColorManagement;
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
new file mode 100644
index 0000000..eb791a7
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+BlurFilter::BlurFilter() {
+ SkString blurString(R"(
+ in shader input;
+ uniform float in_inverseScale;
+ uniform float2 in_blurOffset;
+
+ half4 main(float2 xy) {
+ float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
+
+ float4 c = float4(sample(input, scaled_xy));
+ c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
+ ATRACE_CALL();
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 6.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
+ (float)input->height() * kInputScale);
+ auto drawSurface = canvas->makeSurface(scaledInfo);
+
+ const float stepX = radiusByPasses;
+ const float stepY = radiusByPasses;
+
+ // start by drawing and downscaling and doing the first blur pass
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("input") = input->makeImageSnapshot()->makeShader();
+ blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
+ blurBuilder.uniform("in_blurOffset") =
+ SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+
+ {
+ // limit the lifetime of the input surface's snapshot to ensure that it goes out of
+ // scope before the surface is written into to avoid any copy-on-write behavior.
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr, false));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+ blurBuilder.child("input") = nullptr;
+ }
+
+ // And now we'll ping pong between our surfaces, to accumulate the result of various offsets.
+ auto lastDrawTarget = drawSurface;
+ if (numberOfPasses > 1) {
+ auto readSurface = drawSurface;
+ drawSurface = canvas->makeSurface(scaledInfo);
+
+ for (auto i = 1; i < numberOfPasses; i++) {
+ const float stepScale = (float)i * kInputScale;
+
+ blurBuilder.child("input") = readSurface->makeImageSnapshot()->makeShader();
+ blurBuilder.uniform("in_inverseScale") = 1.0f;
+ blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
+
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr, false));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+
+ // Swap buffers for next iteration
+ auto tmp = drawSurface;
+ drawSurface = readSurface;
+ readSurface = tmp;
+ blurBuilder.child("input") = nullptr;
+ }
+ lastDrawTarget = readSurface;
+ }
+
+ drawSurface->flushAndSubmit();
+
+ // do the final composition, with alpha blending to hide downscaling artifacts.
+ {
+ SkPaint paint;
+ paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
+ SkMatrix::MakeScale(kInverseInputScale)));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+ canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
new file mode 100644
index 0000000..94b3673
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kInputScale = 0.25f;
+ // Downsample scale factor used to improve performance
+ static constexpr float kInverseInputScale = 1.0f / kInputScale;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+ explicit BlurFilter();
+ virtual ~BlurFilter(){};
+
+ // Execute blur passes, rendering to a canvas.
+ void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 6670dc0..58323e5 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -202,6 +202,15 @@
// the input.
Rect transform(uint32_t xform, int32_t width, int32_t height) const;
+ Rect scale(float scaleX, float scaleY) const {
+ return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY));
+ }
+
+ Rect& scaleSelf(float scaleX, float scaleY) {
+ set(scale(scaleX, scaleY));
+ return *this;
+ }
+
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 28ef77a..032dd4c 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -85,6 +85,14 @@
}
cc_test {
+ name: "Rect_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["Rect_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
new file mode 100644
index 0000000..5499a5b
--- /dev/null
+++ b/libs/ui/tests/Rect_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <system/graphics.h>
+#include <ui/FloatRect.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(RectTest, constructDefault) {
+ const Rect rect;
+ EXPECT_FALSE(rect.isValid());
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, constructFromWidthAndHeight) {
+ const Rect rect(100, 200);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromSize) {
+ const Rect rect(Size(100, 200));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromLTRB) {
+ const Rect rect(11, 12, 14, 14);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromPoints) {
+ const Rect rect(Point(11, 12), Point(14, 14));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromFloatRect) {
+ {
+ const Rect rect(FloatRect(10, 20, 30, 40));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+ // Construct with floating point error
+ {
+ constexpr float kError = 1e-3;
+ const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+}
+
+TEST(RectTest, makeInvalid) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_TRUE(rect.isValid());
+ rect.makeInvalid();
+ EXPECT_FALSE(rect.isValid());
+}
+
+TEST(RectTest, clear) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_FALSE(rect.isEmpty());
+ rect.clear();
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, getSize) {
+ const Rect rect(10, 20, 60, 60);
+ EXPECT_EQ(Size(50, 40), rect.getSize());
+}
+
+TEST(RectTest, getBounds) {
+ const Rect rect(10, 20, 60, 60);
+ const Rect bounds = rect.getBounds();
+ EXPECT_EQ(0, bounds.left);
+ EXPECT_EQ(0, bounds.top);
+ EXPECT_EQ(50, bounds.right);
+ EXPECT_EQ(40, bounds.bottom);
+ EXPECT_EQ(rect.getSize(), bounds.getSize());
+}
+
+TEST(RectTest, getCornerPoints) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Point(10, 20), rect.leftTop());
+ EXPECT_EQ(Point(10, 60), rect.leftBottom());
+ EXPECT_EQ(Point(50, 20), rect.rightTop());
+ EXPECT_EQ(Point(50, 60), rect.rightBottom());
+}
+
+TEST(RectTest, operatorEquals) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(rect, rect);
+ EXPECT_NE(Rect(0, 20, 50, 60), rect);
+ EXPECT_NE(Rect(10, 0, 50, 60), rect);
+ EXPECT_NE(Rect(10, 20, 0, 60), rect);
+ EXPECT_NE(Rect(10, 20, 50, 0), rect);
+}
+
+TEST(RectTest, operatorsPlusMinus) {
+ Rect rect = Rect(10, 20, 50, 60) + Point(1, 2);
+ EXPECT_EQ(Rect(11, 22, 51, 62), rect);
+ rect -= Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+
+ rect = Rect(10, 20, 50, 60) - Point(1, 2);
+ EXPECT_EQ(Rect(9, 18, 49, 58), rect);
+ rect += Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+}
+
+TEST(RectTest, scale) {
+ Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f));
+ rect.scaleSelf(2.f, 3.f);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+
+ rect = Rect(10, 20, 50, 60);
+ constexpr float kError = 1e-3;
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError));
+ rect.scaleSelf(2.f - kError, 3.f - kError);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+}
+
+TEST(RectTest, inset) {
+ Rect rect(10, 20, 50, 60);
+ rect.inset(0, 0, 0, 0);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+ rect.inset(1, 2, 3, 4);
+ EXPECT_EQ(Rect(11, 22, 47, 56), rect);
+}
+
+TEST(RectTest, intersect) {
+ const Rect rect(10, 20, 50, 60);
+ Rect intersection;
+
+ // Intersect with self is self
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(10, 20, 50, 60), intersection);
+
+ // Intersect with rect contained in us
+ const Rect insideRect(11, 21, 45, 55);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(insideRect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Intersect with rect we are contained in
+ intersection.makeInvalid();
+ EXPECT_TRUE(insideRect.intersect(rect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Empty intersection
+ intersection.makeInvalid();
+ EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection));
+ EXPECT_TRUE(intersection.isEmpty());
+
+ // Partial intersection
+ const Rect other(30, 40, 70, 80);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(other, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+
+ // Intersetion is commutative
+ intersection.makeInvalid();
+ EXPECT_TRUE(other.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+}
+
+TEST(RectTest, reduce) {
+ const Rect rect(10, 20, 50, 60);
+
+ // Reduce with self is empty
+ EXPECT_TRUE(rect.reduce(rect).isEmpty());
+
+ // Reduce with rect entirely inside is a noop
+ const Rect insideRect(11, 21, 45, 55);
+ EXPECT_EQ(rect, rect.reduce(insideRect));
+
+ // Reduce with rect entirely outside is empty
+ EXPECT_TRUE(insideRect.reduce(rect).isEmpty());
+
+ // Reduce with rect on the right
+ EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70)));
+
+ // Reduce with rect on the left
+ EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70)));
+
+ // Reduce with rect at the top
+ EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40)));
+
+ // Reduce with rect at the bottom
+ EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70)));
+}
+
+TEST(RectTest, transform) {
+ const int32_t width = 100, height = 200;
+ const Rect rect(1, 1, 2, 3);
+ EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height));
+ EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height));
+ EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height));
+ EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height));
+ EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height));
+}
+
+TEST(RectTest, toFloatRect) {
+ const Rect rect(10, 20, 50, 60);
+ const FloatRect floatRect = rect.toFloatRect();
+ EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 38f37ad..5f75aea 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -33,8 +33,7 @@
#include <gtest/gtest.h>
-namespace android {
-namespace ui {
+namespace android::ui {
TEST(SizeTest, BasicConstructionAndEqualityComparison) {
Size s(123, 456);
@@ -215,5 +214,4 @@
ClampTest(uint32_t(0), int32_t(0));
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
index 7fcd7de..eece18e 100644
--- a/libs/ui/tests/TEST_MAPPING
+++ b/libs/ui/tests/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "Size_test"
+ },
+ {
+ "name": "Rect_test"
}
]
}
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 1f1bcb3..e753e0d 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -186,6 +186,26 @@
#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
#endif /* EGL_ANGLE_create_context_extensions_enabled */
+#ifndef EGL_ANGLE_feature_control
+#define EGL_ANGLE_feature_control 1
+#define EGL_FEATURE_NAME_ANGLE 0x3460
+#define EGL_FEATURE_CATEGORY_ANGLE 0x3461
+#define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462
+#define EGL_FEATURE_BUG_ANGLE 0x3463
+#define EGL_FEATURE_STATUS_ANGLE 0x3464
+#define EGL_FEATURE_COUNT_ANGLE 0x3465
+#define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466
+#define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467
+#define EGL_FEATURE_CONDITION_ANGLE 0x3468
+#define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGIANGLEPROC) (EGLDisplay dpy, EGLint name, EGLint index);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBANGLEPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI const char *EGLAPIENTRY eglQueryStringiANGLE(EGLDisplay dpy, EGLint name, EGLint index);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribANGLE(EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#endif
+#endif /* EGL_ANGLE_feature_control */
+
// clang-format on
#endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index a288c21..0b755aa 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -143,6 +143,16 @@
attrs.push_back(attr[1]);
}
}
+ const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
+ std::vector<const char*> features;
+ if (eglFeatures.size() > 0) {
+ for (const std::string& eglFeature : eglFeatures) {
+ features.push_back(eglFeature.c_str());
+ }
+ features.push_back(0);
+ attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
+ attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+ }
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index 45e8367..c5f8859 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -60,7 +60,7 @@
void SetUp() override {
SKIP_IF_BPF_NOT_SUPPORTED;
- ASSERT_EQ(0, bpf::setrlimitForTest());
+ bpf::setrlimitForTest();
mGpuMem = std::make_unique<GpuMem>();
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
new file mode 100644
index 0000000..824c01e
--- /dev/null
+++ b/services/inputflinger/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWindowManagerDeviceTestCases",
+ "options": [
+ {
+ "include-filter": "android.server.wm.WindowInputTests"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index b224476..271bc2f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -85,12 +85,13 @@
bumpGeneration();
}
-void InputDevice::dump(std::string& dump) {
+void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
InputDeviceInfo deviceInfo;
getDeviceInfo(&deviceInfo);
dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getDisplayName().c_str());
+ dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
@@ -102,6 +103,7 @@
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+ dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
if (!ranges.empty()) {
@@ -201,6 +203,8 @@
// insert the context into the devices set
mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+ // Must change generation to flag this device as changed
+ bumpGeneration();
}
void InputDevice::removeEventHubDevice(int32_t eventHubId) {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index feaacb3..7c96e54 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -207,6 +207,14 @@
}
mDevices.emplace(eventHubId, device);
+ // Add device to device to EventHub ids map.
+ const auto mapIt = mDeviceToEventHubIdsMap.find(device);
+ if (mapIt == mDeviceToEventHubIdsMap.end()) {
+ std::vector<int32_t> ids = {eventHubId};
+ mDeviceToEventHubIdsMap.emplace(device, ids);
+ } else {
+ mapIt->second.push_back(eventHubId);
+ }
bumpGenerationLocked();
if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
@@ -223,6 +231,17 @@
std::shared_ptr<InputDevice> device = std::move(deviceIt->second);
mDevices.erase(deviceIt);
+ // Erase device from device to EventHub ids map.
+ auto mapIt = mDeviceToEventHubIdsMap.find(device);
+ if (mapIt != mDeviceToEventHubIdsMap.end()) {
+ std::vector<int32_t>& eventHubIds = mapIt->second;
+ eventHubIds.erase(std::remove_if(eventHubIds.begin(), eventHubIds.end(),
+ [eventHubId](int32_t eId) { return eId == eventHubId; }),
+ eventHubIds.end());
+ if (eventHubIds.size() == 0) {
+ mDeviceToEventHubIdsMap.erase(mapIt);
+ }
+ }
bumpGenerationLocked();
if (device->isIgnored()) {
@@ -463,8 +482,7 @@
void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
outInputDevices.clear();
- for (auto& devicePair : mDevices) {
- std::shared_ptr<InputDevice>& device = devicePair.second;
+ for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) {
if (!device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
@@ -635,11 +653,17 @@
mEventHub->dump(dump);
dump += "\n";
- dump += "Input Reader State:\n";
+ dump += StringPrintf("Input Reader State (Nums of device: %zu):\n",
+ mDeviceToEventHubIdsMap.size());
- for (const auto& devicePair : mDevices) {
- const std::shared_ptr<InputDevice>& device = devicePair.second;
- device->dump(dump);
+ for (const auto& devicePair : mDeviceToEventHubIdsMap) {
+ const std::shared_ptr<InputDevice>& device = devicePair.first;
+ std::string eventHubDevStr = INDENT "EventHub Devices: [ ";
+ for (const auto& eId : devicePair.second) {
+ eventHubDevStr += StringPrintf("%d ", eId);
+ }
+ eventHubDevStr += "] \n";
+ device->dump(dump, eventHubDevStr);
}
dump += INDENT "Configuration:\n";
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 6b28069..7d160eb 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -67,7 +67,7 @@
bool isEnabled();
void setEnabled(bool enabled, nsecs_t when);
- void dump(std::string& dump);
+ void dump(std::string& dump, const std::string& eventHubDevStr);
void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
void removeEventHubDevice(int32_t eventHubId);
void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 2d6ccf5..a902482 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -142,6 +142,11 @@
// to lookup the input device instance from the EventHub device id.
std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
+ // An input device contains one or more eventHubId, this map provides a way to lookup the
+ // EventHubIds contained in the input device from the input device instance.
+ std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
+ mDeviceToEventHubIdsMap;
+
// low-level input event decoding and device management
void processEventsLocked(const RawEvent* rawEvents, size_t count);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 4b0a945..54a36a9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1354,6 +1354,29 @@
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, GetMergedInputDevices) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+ // Push same device instance for next device to be added, so they'll have same identifier.
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[0], "fake1", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+
+ // Two devices will be merged to one input device as they have same identifier
+ std::vector<InputDeviceInfo> inputDevices;
+ mReader->getInputDevices(inputDevices);
+ ASSERT_EQ(1U, inputDevices.size());
+}
+
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 4cbfdff..1cd753b 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -167,7 +167,7 @@
void updateCloneBufferInfo() override;
uint64_t mPreviousFrameNumber = 0;
- virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index ea1f78c..9f99fee 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -258,12 +258,12 @@
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
nsecs_t postTime, nsecs_t desiredPresentTime,
- const client_cache_t& clientCacheId) {
+ const client_cache_t& clientCacheId, uint64_t frameNumber) {
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
}
- mCurrentState.frameNumber++;
+ mCurrentState.frameNumber = frameNumber;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 81959ae..c13f5e8 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,7 +70,8 @@
bool setCrop(const Rect& crop) override;
bool setFrame(const Rect& frame) override;
bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
- nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
+ nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
+ uint64_t frameNumber) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index fc1adcc..d590f23 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -166,7 +166,7 @@
virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) = 0;
// Sets the bounds to use
- virtual void setDisplaySpaceSize(const ui::Size&) = 0;
+ virtual void setDisplaySize(const ui::Size&) = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6fe93bf..5b832a5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -40,7 +40,7 @@
void setCompositionEnabled(bool) override;
void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) override;
- void setDisplaySpaceSize(const ui::Size&) override;
+ void setDisplaySize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f4d2b56..06e6a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -39,7 +39,7 @@
namespace compositionengine::impl {
struct OutputCompositionState {
- // If false, composition will not per performed for this display
+ // If false, composition will not be performed for this display
bool isEnabled{false};
// If false, this output is not considered secure
@@ -71,6 +71,11 @@
// match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
ProjectionSpace orientedDisplaySpace;
+ // The space of the framebuffer. Its bounds match the size of the framebuffer and its
+ // orientation matches the orientation of the display. Typically the framebuffer space will
+ // be identical to the physical display space.
+ ProjectionSpace framebufferSpace;
+
// The space of the physical display. It is as big as the currently active display mode. The
// content in this space can be rotated.
ProjectionSpace displaySpace;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 19025c1..d6fbd7f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -37,7 +37,7 @@
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
- MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
+ MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d201104..5459861 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -55,6 +55,7 @@
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
+ editState().displaySpace.bounds = Rect(args.pixels);
setLayerStackFilter(args.layerStackId,
args.physical ? args.physical->type == DisplayConnectionType::Internal
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index abb8769..04dceae 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -69,6 +69,19 @@
return Reversed<T>(c);
}
+struct ScaleVector {
+ float x;
+ float y;
+};
+
+// Returns a ScaleVector (x, y) such that from.scale(x, y) = to',
+// where to' will have the same size as "to". In the case where "from" and "to"
+// start at the origin to'=to.
+ScaleVector getScale(const Rect& from, const Rect& to) {
+ return {.x = static_cast<float>(to.width()) / from.width(),
+ .y = static_cast<float>(to.height()) / from.height()};
+}
+
} // namespace
std::shared_ptr<Output> createOutput(
@@ -110,9 +123,10 @@
auto& outputState = editState();
outputState.displaySpace.orientation = orientation;
- // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+ LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ "The display bounds are unknown.");
- // Compute the orientedDisplaySpace bounds
+ // Compute orientedDisplaySpace
ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
@@ -129,17 +143,51 @@
}
outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+ // Compute framebufferSpace
+ outputState.framebufferSpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ "The framebuffer bounds are unknown.");
+ const auto scale =
+ getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
+ outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+
+ // Compute layerStackSpace
outputState.layerStackSpace.content = layerStackSpaceRect;
outputState.layerStackSpace.bounds = layerStackSpaceRect;
+
outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
-
dirtyEntireOutput();
}
-void Output::setDisplaySpaceSize(const ui::Size& size) {
+void Output::setDisplaySize(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
+
+ auto& state = editState();
+
+ // Update framebuffer space
+ const Rect newBounds(size);
+ ScaleVector scale;
+ scale = getScale(state.framebufferSpace.bounds, newBounds);
+ state.framebufferSpace.bounds = newBounds;
+ state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
+
+ // Update display space
+ scale = getScale(state.displaySpace.bounds, newBounds);
+ state.displaySpace.bounds = newBounds;
+ state.displaySpace.content.scaleSelf(scale.x, scale.y);
+ state.transform = state.layerStackSpace.getTransform(state.displaySpace);
+
+ // Update oriented display space
+ const auto orientation = state.displaySpace.orientation;
+ ui::Size orientedSize = size;
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ const Rect newOrientedBounds(orientedSize);
+ scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
+ state.orientedDisplaySpace.bounds = newOrientedBounds;
+ state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
dirtyEntireOutput();
}
@@ -247,8 +295,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
-
+ editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -877,7 +924,7 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
clientCompositionDisplay.clip = outputState.layerStackSpace.content;
clientCompositionDisplay.orientation =
ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 776fdde..ee30ad8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -39,6 +39,8 @@
out.append("\n ");
dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
out.append("\n ");
+ dumpVal(out, "framebufferSpace", to_string(framebufferSpace));
+ out.append("\n ");
dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
out.append("\n ");
dumpVal(out, "displaySpace", to_string(displaySpace));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c01f3e0..1e10365 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -235,33 +235,121 @@
* Output::setProjection()
*/
-TEST_F(OutputTest, setProjectionTriviallyWorks) {
+TEST_F(OutputTest, setProjectionWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = displayRect;
+
const ui::Rotation orientation = ui::ROTATION_90;
- const Rect frame{1, 2, 3, 4};
- const Rect viewport{5, 6, 7, 8};
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
mOutput->setProjection(orientation, viewport, frame);
EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+}
+
+TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ const Rect framebufferRect{0, 0, 500, 1000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = framebufferRect;
+
+ const ui::Rotation orientation = ui::ROTATION_90;
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
+
+ mOutput->setProjection(orientation, viewport, frame);
+
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
}
/*
- * Output::setDisplaySpaceSize()
+ * Output::setDisplaySize()
*/
-TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
- const ui::Size displaySize{200, 400};
+TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
+ mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
+ mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
+ mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
+ mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
- EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
- EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+ const ui::Size newDisplaySize{500, 1000};
- mOutput->setDisplaySpaceSize(displaySize);
+ EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
+ mOutput->setDisplaySize(newDisplaySize);
- EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+ const auto state = mOutput->getState();
+
+ const Rect displayRect(newDisplaySize);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
/*
@@ -422,7 +510,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
}
/*
@@ -2778,9 +2866,10 @@
mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
mOutput.mState.isSecure = false;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 7df9b76..a1ccaad 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -156,7 +156,8 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setDisplaySpaceSize(ui::Size(width, height));
+ LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
+ mCompositionDisplay->setDisplaySize(ui::Size(width, height));
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 85046a4..58291d4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -800,10 +800,10 @@
// to be applied as per normal (no synchronization).
mCurrentState.barrierLayer_legacy = nullptr;
} else {
- auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy, this);
+ auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.barrierFrameNumber, this);
if (barrierLayer->addSyncPoint(syncPoint)) {
std::stringstream ss;
- ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
+ ss << "Adding sync point " << mCurrentState.barrierFrameNumber;
ATRACE_NAME(ss.str().c_str());
mRemoteSyncPoints.push_back(std::move(syncPoint));
} else {
@@ -844,7 +844,7 @@
}
if (mRemoteSyncPoints.front()->getFrameNumber() !=
- mPendingStates[0].frameNumber_legacy) {
+ mPendingStates[0].barrierFrameNumber) {
ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
// Signal our end of the sync point and then dispose of it
@@ -1463,13 +1463,13 @@
}
mCurrentState.barrierLayer_legacy = barrierLayer;
- mCurrentState.frameNumber_legacy = frameNumber;
+ mCurrentState.barrierFrameNumber = frameNumber;
// We don't set eTransactionNeeded, because just receiving a deferral
// request without any other state updates shouldn't actually induce a delay
mCurrentState.modified = true;
pushPendingState();
mCurrentState.barrierLayer_legacy = nullptr;
- mCurrentState.frameNumber_legacy = 0;
+ mCurrentState.barrierFrameNumber = 0;
mCurrentState.modified = false;
}
@@ -2234,7 +2234,7 @@
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
const DisplayDevice* display) {
- ui::Transform transform = getTransform();
+ const ui::Transform transform = getTransform();
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
for (const auto& pendingState : mPendingStatesSnapshot) {
@@ -2242,7 +2242,7 @@
if (barrierLayer != nullptr) {
BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
barrierLayerProto->set_id(barrierLayer->sequence);
- barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
+ barrierLayerProto->set_frame_number(pendingState.barrierFrameNumber);
}
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 88ece50..bdc4f84 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -187,7 +187,7 @@
// If set, defers this state update until the identified Layer
// receives a frame with the given frameNumber
wp<Layer> barrierLayer_legacy;
- uint64_t frameNumber_legacy;
+ uint64_t barrierFrameNumber;
// the transparentRegion hint is a bit special, it's latched only
// when we receive a buffer -- this is because it's "content"
@@ -406,7 +406,7 @@
virtual bool setFrame(const Rect& /*frame*/) { return false; };
virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
- const client_cache_t& /*clientCacheId*/) {
+ const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -510,6 +510,8 @@
virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
+ virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
+
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d8477e7..99d061d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -241,7 +241,8 @@
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
mCurrentFps = refreshRate.getFps();
auto buffer = mBufferCache[*mCurrentFps][mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
@@ -252,7 +253,8 @@
const auto& buffers = mBufferCache[*mCurrentFps];
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 3307388..bf2a509 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -62,6 +62,8 @@
return "VSyncRequest::None";
case VSyncRequest::Single:
return "VSyncRequest::Single";
+ case VSyncRequest::SingleSuppressCallback:
+ return "VSyncRequest::SingleSuppressCallback";
default:
return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
}
@@ -267,6 +269,8 @@
if (connection->vsyncRequest == VSyncRequest::None) {
connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
+ } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
+ connection->vsyncRequest = VSyncRequest::Single;
}
}
@@ -451,8 +455,11 @@
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
case VSyncRequest::Periodic:
return true;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 80bd606..e42ca05 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -50,8 +50,11 @@
using ResyncCallback = std::function<void()>;
enum class VSyncRequest {
- None = -1,
- Single = 0,
+ None = -2,
+ // Single wakes up for the next two frames to avoid scheduler overhead
+ Single = -1,
+ // SingleSuppressCallback only wakes up for the next frame
+ SingleSuppressCallback = 0,
Periodic = 1,
// Subsequent values are periods.
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 9c145cc..7b8448f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -229,6 +229,7 @@
auto connection =
createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
return handle;
}
@@ -240,29 +241,47 @@
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return mConnections[handle].connection;
}
void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
bool connected) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onHotplugReceived(displayId, connected);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+
+ thread->onHotplugReceived(displayId, connected);
}
void Scheduler::onScreenAcquired(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenAcquired();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenAcquired();
}
void Scheduler::onScreenReleased(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenReleased();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenReleased();
}
void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
@@ -274,6 +293,16 @@
}
void Scheduler::dispatchCachedReportedConfig() {
+ // Check optional fields first.
+ if (!mFeatures.configId.has_value()) {
+ ALOGW("No config ID found, not dispatching cached config.");
+ return;
+ }
+ if (!mFeatures.cachedConfigChangedParams.has_value()) {
+ ALOGW("No config changed params found, not dispatching cached config.");
+ return;
+ }
+
const auto configId = *mFeatures.configId;
const auto vsyncPeriod =
mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
@@ -295,24 +324,40 @@
void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
PhysicalDisplayId displayId,
HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onConfigChanged(displayId, configId, vsyncPeriod);
}
size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, 0);
return mConnections[handle].thread->getEventThreadConnectionCount();
}
void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections.at(handle).thread->dump(result);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections.at(handle).thread.get();
+ }
+ thread->dump(result);
}
void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->setDuration(workDuration, readyDuration);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->setDuration(workDuration, readyDuration);
}
void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index da25f5c..3ecda58 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -228,7 +228,8 @@
};
ConnectionHandle::Id mNextConnectionHandleId = 0;
- std::unordered_map<ConnectionHandle, Connection> mConnections;
+ mutable std::mutex mConnectionsLock;
+ std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
bool mInjectVSyncs = false;
InjectVSyncSource* mVSyncInjector = nullptr;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 524f4c6..83fa8ec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1982,7 +1982,7 @@
mFrameTimeline->setSfPresent(systemTime(),
std::make_shared<FenceTime>(mPreviousPresentFences[0]));
- const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
+ const bool prevFrameHadClientComposition = mHadClientComposition;
mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
const auto& state = pair.second->getCompositionDisplay()->getState();
@@ -1997,9 +1997,8 @@
const auto& state = pair.second->getCompositionDisplay()->getState();
return state.reusedClientComposition;
});
-
- // Only report a strategy change if we move in and out of composition with hw overlays
- if (prevFrameHadDeviceComposition != mHadDeviceComposition) {
+ // Only report a strategy change if we move in and out of client composition
+ if (prevFrameHadClientComposition != mHadClientComposition) {
mTimeStats->incrementCompositionStrategyChanges();
}
@@ -3682,13 +3681,13 @@
}
if (what & layer_state_t::eDeferTransaction_legacy) {
if (s.barrierHandle_legacy != nullptr) {
- layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
+ layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.barrierFrameNumber);
} else if (s.barrierGbp_legacy != nullptr) {
const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
if (authenticateSurfaceTextureLocked(gbp)) {
const auto& otherLayer =
(static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
+ layer->deferTransactionUntil_legacy(otherLayer, s.barrierFrameNumber);
} else {
ALOGE("Attempt to defer transaction to to an"
" unrecognized GraphicBufferProducer");
@@ -3819,8 +3818,13 @@
buffer = s.buffer;
}
if (buffer) {
- if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
- s.cachedBuffer)) {
+ const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
+ const uint64_t frameNumber = frameNumberChanged
+ ? s.frameNumber
+ : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
+
+ if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
+ frameNumber)) {
flags |= eTraversalNeeded;
}
}
@@ -6191,6 +6195,11 @@
}
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame timeline vsync on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
layer->setFrameTimelineVsync(frameTimelineVsyncId);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 016951a..4407f4c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -148,7 +148,7 @@
if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
addDeferTransactionLocked(transaction, layerId,
layer->mCurrentState.barrierLayer_legacy.promote(),
- layer->mCurrentState.frameNumber_legacy);
+ layer->mCurrentState.barrierFrameNumber);
}
addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
@@ -483,7 +483,7 @@
ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
}
}
- addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
+ addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber);
}
if (state.what & layer_state_t::eOverrideScalingModeChanged) {
addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b750d6b..5864d02 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1448,7 +1448,7 @@
});
}
- ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+ ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
void setProjectionForRotation0() {
// A logical rotation of 0 uses the SurfaceFlinger display size
@@ -1459,8 +1459,8 @@
void setProjectionForRotation90() {
// A logical rotation of 90 uses the SurfaceFlinger display size with
// the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
+ mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
}
void setProjectionForRotation180() {
@@ -1472,8 +1472,8 @@
void setProjectionForRotation270() {
// A logical rotation of 270 uses the SurfaceFlinger display size with
// the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
+ mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
}
void expectStateForHardwareTransform0() {
@@ -1497,9 +1497,9 @@
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
// For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1523,9 +1523,9 @@
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
// For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 35619a3..eee9400 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -175,4 +175,25 @@
mScheduler.chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, testDispatchCachedReportedConfig) {
+ // If the optional fields are cleared, the function should return before
+ // onConfigChange is called.
+ mScheduler.clearOptionalFieldsInFeatures();
+ EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedConfig());
+ EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) {
+ HwcConfigIndexType configId = HwcConfigIndexType(111);
+ nsecs_t vsyncPeriod = 111111;
+
+ // If the handle is incorrect, the function should return before
+ // onConfigChange is called.
+ Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+ EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayConfigChanged(invalidHandle,
+ PHYSICAL_DISPLAY_ID,
+ configId, vsyncPeriod));
+ EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index db3e0bd..a9d9dc0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -16,6 +16,7 @@
#pragma once
+#include <Scheduler/Scheduler.h>
#include <gmock/gmock.h>
#include <gui/ISurfaceComposer.h>
@@ -93,6 +94,22 @@
return mFeatures.touch == Scheduler::TouchState::Active;
}
+ void dispatchCachedReportedConfig() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return Scheduler::dispatchCachedReportedConfig();
+ }
+
+ void clearOptionalFieldsInFeatures() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mFeatures.cachedConfigChangedParams.reset();
+ }
+
+ void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId,
+ vsyncPeriod);
+ }
+
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the Scheduler instance may