Merge "Add a simple ring buffer and use it for holding TFLite model inputs."
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 695faf8..07809e2 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -39,6 +39,11 @@
namespace android {
+bool is_multiuser_uid_isolated(uid_t uid) {
+ uid_t appid = multiuser_get_app_id(uid);
+ return appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
+}
+
#ifndef VENDORSERVICEMANAGER
struct ManifestWithDescription {
@@ -222,6 +227,13 @@
}
#endif // !VENDORSERVICEMANAGER
+ServiceManager::Service::~Service() {
+ if (!hasClients) {
+ // only expected to happen on process death
+ LOG(WARNING) << "a service was removed when there are clients";
+ }
+}
+
ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {
// TODO(b/151696835): reenable performance hack when we solve bug, since with
// this hack and other fixes, it is unlikely we will see even an ephemeral
@@ -273,13 +285,8 @@
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
service = &(it->second);
- if (!service->allowIsolated) {
- uid_t appid = multiuser_get_app_id(ctx.uid);
- bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
-
- if (isIsolated) {
- return nullptr;
- }
+ if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
+ return nullptr;
}
out = service->binder;
}
@@ -293,8 +300,13 @@
}
if (out) {
- // Setting this guarantee each time we hand out a binder ensures that the client-checking
- // loop knows about the event even if the client immediately drops the service
+ // Force onClients to get sent, and then make sure the timerfd won't clear it
+ // by setting guaranteeClient again. This logic could be simplified by using
+ // a time-based guarantee. However, forcing onClients(true) to get sent
+ // right here is always going to be important for processes serving multiple
+ // lazy interfaces.
+ service->guaranteeClient = true;
+ CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
service->guaranteeClient = true;
}
@@ -384,8 +396,13 @@
};
if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
+ // See also getService - handles case where client never gets the service,
+ // we want the service to quit.
+ mNameToService[name].guaranteeClient = true;
+ CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
+ mNameToService[name].guaranteeClient = true;
+
for (const sp<IServiceCallback>& cb : it->second) {
- mNameToService[name].guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
@@ -425,7 +442,17 @@
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux");
+ }
+
+ // note - we could allow isolated apps to get notifications if we
+ // keep track of isolated callbacks and non-isolated callbacks, but
+ // this is done since isolated apps shouldn't access lazy services
+ // so we should be able to use different APIs to keep things simple.
+ // Here, we disallow everything, because the service might not be
+ // registered yet.
+ if (is_multiuser_uid_isolated(ctx.uid)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "isolated app");
}
if (!isValidServiceName(name)) {
@@ -696,28 +723,28 @@
void ServiceManager::handleClientCallbacks() {
for (const auto& [name, service] : mNameToService) {
- handleServiceClientCallback(name, true);
+ handleServiceClientCallback(1 /* sm has one refcount */, name, true);
}
}
-ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName,
- bool isCalledOnInterval) {
+bool ServiceManager::handleServiceClientCallback(size_t knownClients,
+ const std::string& serviceName,
+ bool isCalledOnInterval) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
- return -1;
+ return true; // return we do have clients a.k.a. DON'T DO ANYTHING
}
Service& service = serviceIt->second;
ssize_t count = service.getNodeStrongRefCount();
- // binder driver doesn't support this feature
- if (count == -1) return count;
+ // binder driver doesn't support this feature, consider we have clients
+ if (count == -1) return true;
- bool hasClients = count > 1; // this process holds a strong count
+ bool hasKernelReportedClients = static_cast<size_t>(count) > knownClients;
if (service.guaranteeClient) {
- // we have no record of this client
- if (!service.hasClients && !hasClients) {
+ if (!service.hasClients && !hasKernelReportedClients) {
sendClientCallbackNotifications(serviceName, true,
"service is guaranteed to be in use");
}
@@ -726,21 +753,23 @@
service.guaranteeClient = false;
}
- // only send notifications if this was called via the interval checking workflow
- if (isCalledOnInterval) {
- if (hasClients && !service.hasClients) {
- // client was retrieved in some other way
- sendClientCallbackNotifications(serviceName, true, "we now have a record of a client");
- }
+ // Regardless of this situation, we want to give this notification as soon as possible.
+ // This way, we have a chance of preventing further thrashing.
+ if (hasKernelReportedClients && !service.hasClients) {
+ sendClientCallbackNotifications(serviceName, true, "we now have a record of a client");
+ }
- // there are no more clients, but the callback has not been called yet
- if (!hasClients && service.hasClients) {
+ // But limit rate of shutting down service.
+ if (isCalledOnInterval) {
+ if (!hasKernelReportedClients && service.hasClients) {
sendClientCallbackNotifications(serviceName, false,
"we now have no record of a client");
}
}
- return count;
+ // May be different than 'hasKernelReportedClients'. We intentionally delay
+ // information about clients going away to reduce thrashing.
+ return service.hasClients;
}
void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName,
@@ -753,13 +782,10 @@
}
Service& service = serviceIt->second;
- CHECK(hasClients != service.hasClients)
- << "Record shows: " << service.hasClients
- << " so we can't tell clients again that we have client: " << hasClients
- << " when: " << context;
+ CHECK_NE(hasClients, service.hasClients) << context;
- ALOGI("Notifying %s they %s have clients when %s", serviceName.c_str(),
- hasClients ? "do" : "don't", context);
+ ALOGI("Notifying %s they %s (previously: %s) have clients when %s", serviceName.c_str(),
+ hasClients ? "do" : "don't", service.hasClients ? "do" : "don't", context);
auto ccIt = mNameToClientCallback.find(serviceName);
CHECK(ccIt != mNameToClientCallback.end())
@@ -803,26 +829,29 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
+ // important because we don't have timer-based guarantees, we don't want to clear
+ // this
if (serviceIt->second.guaranteeClient) {
ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- int clients = handleServiceClientCallback(name, false);
-
- // clients < 0: feature not implemented or other error. Assume clients.
- // Otherwise:
// - kernel driver will hold onto one refcount (during this transaction)
// - servicemanager has a refcount (guaranteed by this transaction)
- // So, if clients > 2, then at least one other service on the system must hold a refcount.
- if (clients < 0 || clients > 2) {
- // client callbacks are either disabled or there are other clients
- ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients);
- // Set this flag to ensure the clients are acknowledged in the next callback
+ constexpr size_t kKnownClients = 2;
+
+ if (handleServiceClientCallback(kKnownClients, name, false)) {
+ ALOGI("Tried to unregister %s, but there are clients.", name.c_str());
+
+ // Since we had a failed registration attempt, and the HIDL implementation of
+ // delaying service shutdown for multiple periods wasn't ported here... this may
+ // help reduce thrashing, but we should be able to remove it.
serviceIt->second.guaranteeClient = true;
+
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
+ ALOGI("Unregistering %s", name.c_str());
mNameToService.erase(name);
return Status::ok();
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index f9d4f8f..3aa6731 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -80,6 +80,8 @@
// the number of clients of the service, including servicemanager itself
ssize_t getNodeStrongRefCount();
+
+ ~Service();
};
using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
@@ -91,7 +93,9 @@
void removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found);
- ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
+ // returns whether there are known clients in addition to the count provided
+ bool handleServiceClientCallback(size_t knownClients, const std::string& serviceName,
+ bool isCalledOnInterval);
// Also updates mHasClients (of what the last callback was)
void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients,
const char* context);
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 0fd8d8e..cae32e3 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -383,6 +383,22 @@
sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
+ EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(), Status::EX_SECURITY);
+}
+
+TEST(GetService, IsolatedCantRegister) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext())
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
+ EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
+
+ sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
+
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
+
EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
Status::EX_SECURITY);
}
diff --git a/include/input/Input.h b/include/input/Input.h
index 7573282..608519b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1113,6 +1113,7 @@
enum class PointerIconStyle : int32_t {
TYPE_CUSTOM = -1,
TYPE_NULL = 0,
+ TYPE_NOT_SPECIFIED = 1,
TYPE_ARROW = 1000,
TYPE_CONTEXT_MENU = 1001,
TYPE_HAND = 1002,
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index eaf3b5e..d50c5f8 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
+#include <stdint.h>
+
__BEGIN_DECLS
/**
@@ -27,7 +29,7 @@
/**
* Hints for the session used to signal upcoming changes in the mode or workload.
*/
-enum SessionHint {
+enum SessionHint: int32_t {
/**
* This hint indicates a sudden increase in CPU workload intensity. It means
* that this hint session needs extra CPU resources immediately to meet the
@@ -61,7 +63,7 @@
* @return 0 on success
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(void* session, int hint);
+int APerformanceHint_sendHint(void* session, SessionHint hint);
/**
* Return the list of thread ids, this API should only be used for testing only.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7e7bba3..661f70c 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -287,6 +287,14 @@
cflags: [
"-DBINDER_WITH_KERNEL_IPC",
],
+ arch: {
+ // TODO(b/254713216): undefined symbol in BufferedTextOutput::getBuffer
+ riscv64: {
+ lto: {
+ thin: false,
+ },
+ },
+ },
}
cc_library {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 04cb61f..1488400 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -86,6 +86,12 @@
"name": "binderRpcTest"
},
{
+ "name": "CtsRootRollbackManagerHostTestCases"
+ },
+ {
+ "name": "StagedRollbackTest"
+ },
+ {
"name": "binderRpcTestNoKernel"
},
{
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 60603ba..5d12463 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -432,8 +432,8 @@
mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
}
- const auto numPendingBuffersToHold =
- isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
+ const uint32_t numPendingBuffersToHold =
+ isEGL ? std::max(0, mMaxAcquiredBuffers - (int32_t)mCurrentMaxAcquiredBufferCount) : 0;
auto rb = ReleasedBuffer{id, releaseFence};
if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 6b25b26..99bf6ba 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -101,8 +101,9 @@
return gChoreographer;
}
-Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
+Choreographer::Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle)
+ : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, {},
+ layerHandle),
mLooper(looper),
mThreadId(std::this_thread::get_id()) {
std::lock_guard<std::mutex> _l(gChoreographers.lock);
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 501e69a..8a88377 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -37,9 +37,10 @@
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
gui::ISurfaceComposer::VsyncSource vsyncSource,
- EventRegistrationFlags eventRegistration)
+ EventRegistrationFlags eventRegistration,
+ const sp<IBinder>& layerHandle)
: mLooper(looper),
- mReceiver(vsyncSource, eventRegistration),
+ mReceiver(vsyncSource, eventRegistration, layerHandle),
mWaitingForVsync(false),
mLastVsyncCount(0),
mLastScheduleVsyncTime(0) {
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index c52fb6b..6849a95 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "DisplayEventReceiver"
+
#include <string.h>
#include <utils/Errors.h>
@@ -32,7 +34,8 @@
// ---------------------------------------------------------------------------
DisplayEventReceiver::DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource vsyncSource,
- EventRegistrationFlags eventRegistration) {
+ EventRegistrationFlags eventRegistration,
+ const sp<IBinder>& layerHandle) {
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
if (sf != nullptr) {
mEventConnection = nullptr;
@@ -41,8 +44,8 @@
static_cast<
gui::ISurfaceComposer::EventRegistration>(
eventRegistration.get()),
- &mEventConnection);
- if (mEventConnection != nullptr) {
+ layerHandle, &mEventConnection);
+ if (status.isOk() && mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
status = mEventConnection->stealReceiveChannel(mDataChannel.get());
if (!status.isOk()) {
@@ -51,6 +54,8 @@
mDataChannel.reset();
mEventConnection.clear();
}
+ } else {
+ ALOGE("DisplayEventConnection creation failed: status=%s", status.toString8().c_str());
}
}
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 7aee882..c5f9c38 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -26,6 +26,7 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Log.h>
+#include <utils/Looper.h>
#include <utils/threads.h>
#include <binder/IPCThreadState.h>
@@ -34,8 +35,9 @@
#include <ui/Rect.h>
#include <ui/StaticDisplayInfo.h>
-#include <gui/BufferQueueCore.h>
#include <gui/BLASTBufferQueue.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/Choreographer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -191,6 +193,24 @@
return mName;
}
+std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() {
+ if (mChoreographer) {
+ return mChoreographer;
+ }
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ ALOGE("%s: No looper prepared for thread", __func__);
+ return nullptr;
+ }
+ mChoreographer = std::make_shared<Choreographer>(looper, getHandle());
+ status_t result = mChoreographer->initialize();
+ if (result != OK) {
+ ALOGE("Failed to initialize choreographer");
+ mChoreographer = nullptr;
+ }
+ return mChoreographer;
+}
+
sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer()
{
getSurface();
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 597749a..9812142 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -68,9 +68,15 @@
/**
* Create a display event connection
+ *
+ * layerHandle
+ * Optional binder handle representing a Layer in SF to associate the new
+ * DisplayEventConnection with. This handle can be found inside a surface control after
+ * surface creation, see ISurfaceComposerClient::createSurface. Set to null if no layer
+ * association should be made.
*/
@nullable IDisplayEventConnection createDisplayEventConnection(VsyncSource vsyncSource,
- EventRegistration eventRegistration);
+ EventRegistration eventRegistration, @nullable IBinder layerHandle);
/**
* Create a connection with SurfaceFlinger.
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 14a0e39..f01c2a9 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -64,7 +64,7 @@
MOCK_METHOD(binder::Status, bootFinished, (), (override));
MOCK_METHOD(binder::Status, createDisplayEventConnection,
(gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration,
- sp<gui::IDisplayEventConnection>*),
+ const sp<IBinder>& /*layerHandle*/, sp<gui::IDisplayEventConnection>*),
(override));
MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override));
MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, float, sp<IBinder>*),
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 89a7058..1df9b11 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -73,7 +73,8 @@
};
static Context gChoreographers;
- explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
+ explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr)
+ EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index bf3a07b..140efa6 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -26,7 +26,8 @@
explicit DisplayEventDispatcher(const sp<Looper>& looper,
gui::ISurfaceComposer::VsyncSource vsyncSource =
gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- EventRegistrationFlags eventRegistration = {});
+ EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
status_t initialize();
void dispose();
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0f4907f..7fd6c35 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -119,7 +119,8 @@
*/
explicit DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource vsyncSource =
gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- EventRegistrationFlags eventRegistration = {});
+ EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 1d4fc7f..344b957 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -36,6 +36,7 @@
// ---------------------------------------------------------------------------
+class Choreographer;
class IGraphicBufferProducer;
class Surface;
class SurfaceComposerClient;
@@ -80,6 +81,9 @@
int32_t getLayerId() const;
const std::string& getName() const;
+ // TODO(b/267195698): Consider renaming.
+ std::shared_ptr<Choreographer> getChoreographer();
+
sp<IGraphicBufferProducer> getIGraphicBufferProducer();
status_t clearLayerFrameStats() const;
@@ -130,6 +134,7 @@
PixelFormat mFormat = PIXEL_FORMAT_NONE;
uint32_t mCreateFlags = 0;
uint64_t mFallbackFrameNumber = 100;
+ std::shared_ptr<Choreographer> mChoreographer;
};
}; // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 9b2bf7f..babc197 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -725,6 +725,7 @@
binder::Status createDisplayEventConnection(
VsyncSource /*vsyncSource*/, EventRegistration /*eventRegistration*/,
+ const sp<IBinder>& /*layerHandle*/,
sp<gui::IDisplayEventConnection>* outConnection) override {
*outConnection = nullptr;
return binder::Status::ok();
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 1a4b679..aee6602 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -21,6 +21,7 @@
namespace android::recoverymap {
+// Color gamuts for image data
typedef enum {
JPEGR_COLORGAMUT_UNSPECIFIED,
JPEGR_COLORGAMUT_BT709,
@@ -28,7 +29,7 @@
JPEGR_COLORGAMUT_BT2100,
} jpegr_color_gamut;
-// Transfer functions as defined for XMP metadata
+// Transfer functions for image data
typedef enum {
JPEGR_TF_UNSPECIFIED = -1,
JPEGR_TF_LINEAR = 0,
@@ -82,45 +83,11 @@
int length;
};
-struct chromaticity_coord {
- float x;
- float y;
-};
-
-
-struct st2086_metadata {
- // xy chromaticity coordinate of the red primary of the mastering display
- chromaticity_coord redPrimary;
- // xy chromaticity coordinate of the green primary of the mastering display
- chromaticity_coord greenPrimary;
- // xy chromaticity coordinate of the blue primary of the mastering display
- chromaticity_coord bluePrimary;
- // xy chromaticity coordinate of the white point of the mastering display
- chromaticity_coord whitePoint;
- // Maximum luminance in nits of the mastering display
- uint32_t maxLuminance;
- // Minimum luminance in nits of the mastering display
- float minLuminance;
-};
-
-struct hdr10_metadata {
- // Mastering display color volume
- st2086_metadata st2086Metadata;
- // Max frame average light level in nits
- float maxFALL;
- // Max content light level in nits
- float maxCLL;
-};
-
struct jpegr_metadata {
// JPEG/R version
uint32_t version;
- // Range scaling factor for the map
- float rangeScalingFactor;
- // The transfer function for decoding the HDR representation of the image
- jpegr_transfer_function transferFunction;
- // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
- hdr10_metadata hdr10Metadata;
+ // Max Content Boost for the map
+ float maxContentBoost;
};
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
@@ -270,14 +237,14 @@
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
* @param dest recovery map; caller responsible for memory of data
- * @param metadata metadata provides the transfer function for the HDR
- * image; range_scaling_factor and hdr10 FALL and CLL will
- * be updated.
+ * @param metadata max_content_boost is filled in
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
jr_metadata_ptr metadata,
jr_uncompressed_ptr dest);
@@ -285,8 +252,7 @@
* This method is called in the decoding pipeline. It will take the uncompressed (decoded)
* 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
* input, and calculate the 10-bit recovered image. The recovered output image is the same
- * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
- * and is in RGBA1010102 data format.
+ * color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_recovery_map uncompressed recovery map
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index 8696851..de29a33 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -55,7 +55,7 @@
*
* below is an example of the XMP metadata that this function generates where
* secondary_image_length = 1000
- * range_scaling_factor = 1.25
+ * max_content_boost = 8.0
*
* <x:xmpmeta
* xmlns:x="adobe:ns:meta/"
@@ -63,31 +63,26 @@
* <rdf:RDF
* xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
* <rdf:Description
- * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"
+ * xmlns:Container="http://ns.google.com/photos/1.0/container/"
+ * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
* xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
- * <GContainer:Version>1</GContainer:Version>
- * <GContainer:Directory>
+ * <Container:Directory>
* <rdf:Seq>
* <rdf:li>
- * <GContainer:Item
- * GContainer:ItemSemantic="Primary"
- * GContainer:ItemMime="image/jpeg"
- * RecoveryMap:Version=”1”
- * RecoveryMap:RangeScalingFactor=”1.25”
- * RecoveryMap:TransferFunction=”2”/>
- * <RecoveryMap:HDR10Metadata
- * // some attributes
- * // some elements
- * </RecoveryMap:HDR10Metadata>
+ * <Container:Item
+ * Item:Semantic="Primary"
+ * Item:Mime="image/jpeg"
+ * RecoveryMap:Version="1"
+ * RecoveryMap:MaxContentBoost="8.0"/>
* </rdf:li>
* <rdf:li>
- * <GContainer:Item
- * GContainer:ItemSemantic="RecoveryMap"
- * GContainer:ItemMime="image/jpeg"
- * GContainer:ItemLength="1000"/>
+ * <Container:Item
+ * Item:Semantic="RecoveryMap"
+ * Item:Mime="image/jpeg"
+ * Item:Length="1000"/>
* </rdf:li>
* </rdf:Seq>
- * </GContainer:Directory>
+ * </Container:Directory>
* </rdf:Description>
* </rdf:RDF>
* </x:xmpmeta>
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index e06bd24..8b8c2e7 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -72,16 +72,6 @@
// JPEG compress quality (0 ~ 100) for recovery map
static const int kMapCompressQuality = 85;
-// TODO: fill in st2086 metadata
-static const st2086_metadata kSt2086Metadata = {
- {0.0f, 0.0f},
- {0.0f, 0.0f},
- {0.0f, 0.0f},
- {0.0f, 0.0f},
- 0,
- 1.0f,
-};
-
#define CONFIG_MULTITHREAD 1
int GetCPUCoreCount() {
int cpuCoreCount = 1;
@@ -133,10 +123,6 @@
jpegr_metadata metadata;
metadata.version = kJpegrVersion;
- metadata.transferFunction = hdr_tf;
- if (hdr_tf == JPEGR_TF_PQ) {
- metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
- }
jpegr_uncompressed_struct uncompressed_yuv_420_image;
unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
@@ -146,7 +132,7 @@
jpegr_uncompressed_struct map;
JPEGR_CHECK(generateRecoveryMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -207,14 +193,10 @@
jpegr_metadata metadata;
metadata.version = kJpegrVersion;
- metadata.transferFunction = hdr_tf;
- if (hdr_tf == JPEGR_TF_PQ) {
- metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
- }
jpegr_uncompressed_struct map;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -271,14 +253,10 @@
jpegr_metadata metadata;
metadata.version = kJpegrVersion;
- metadata.transferFunction = hdr_tf;
- if (hdr_tf == JPEGR_TF_PQ) {
- metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
- }
jpegr_uncompressed_struct map;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -328,14 +306,10 @@
jpegr_metadata metadata;
metadata.version = kJpegrVersion;
- metadata.transferFunction = hdr_tf;
- if (hdr_tf == JPEGR_TF_PQ) {
- metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
- }
jpegr_uncompressed_struct map;
JPEGR_CHECK(generateRecoveryMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -437,7 +411,6 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: should we have ICC data for the map?
JpegEncoder jpeg_encoder;
if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
uncompressed_recovery_map->width,
@@ -518,6 +491,7 @@
status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
jr_metadata_ptr metadata,
jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
@@ -554,7 +528,7 @@
ColorTransformFn hdrInvOetf = nullptr;
float hdr_white_nits = 0.0f;
- switch (metadata->transferFunction) {
+ switch (hdr_tf) {
case JPEGR_TF_LINEAR:
hdrInvOetf = identityConversion;
break;
@@ -637,9 +611,11 @@
metadata, dest, hdrInvOetf, hdrGamutConversionFn,
luminanceFn, hdr_white_nits, &jobQueue]() -> void {
size_t rowStart, rowEnd;
+ size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
+ size_t dest_map_stride = dest->width;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
- for (size_t x = 0; x < dest->width; ++x) {
+ for (size_t x = 0; x < dest_map_width; ++x) {
Color sdr_yuv_gamma =
sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
@@ -656,9 +632,9 @@
hdr_rgb = hdrGamutConversionFn(hdr_rgb);
float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
- size_t pixel_idx = x + y * dest->width;
+ size_t pixel_idx = x + y * dest_map_stride;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
+ encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost);
}
}
}
@@ -681,11 +657,7 @@
workers.clear();
hdr_y_nits_avg /= image_width * image_height;
- metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
- if (metadata->transferFunction == JPEGR_TF_PQ) {
- metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
- metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
- }
+ metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits;
// generate map
jobQueue.reset();
@@ -721,39 +693,21 @@
dest->width = uncompressed_yuv_420_image->width;
dest->height = uncompressed_yuv_420_image->height;
ShepardsIDW idwTable(kMapDimensionScaleFactor);
- RecoveryLUT recoveryLUT(metadata->rangeScalingFactor);
+ RecoveryLUT recoveryLUT(metadata->maxContentBoost);
JobQueue jobQueue;
std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
metadata, dest, &jobQueue, &idwTable,
&recoveryLUT]() -> void {
- const float hdr_ratio = metadata->rangeScalingFactor;
+ const float hdr_ratio = metadata->maxContentBoost;
size_t width = uncompressed_yuv_420_image->width;
size_t height = uncompressed_yuv_420_image->height;
- ColorTransformFn hdrOetf = nullptr;
- switch (metadata->transferFunction) {
- case JPEGR_TF_LINEAR:
- hdrOetf = identityConversion;
- break;
- case JPEGR_TF_HLG:
#if USE_HLG_OETF_LUT
- hdrOetf = hlgOetfLUT;
+ ColorTransformFn hdrOetf = hlgOetfLUT;
#else
- hdrOetf = hlgOetf;
+ ColorTransformFn hdrOetf = hlgOetf;
#endif
- break;
- case JPEGR_TF_PQ:
-#if USE_PQ_OETF_LUT
- hdrOetf = pqOetfLUT;
-#else
- hdrOetf = pqOetf;
-#endif
- break;
- default:
- // Should be impossible to hit after input validation.
- hdrOetf = identityConversion;
- }
size_t rowStart, rowEnd;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
@@ -783,7 +737,7 @@
#else
Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
#endif
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
size_t pixel_idx = x + y * width;
@@ -811,8 +765,8 @@
}
status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr primary_image,
- jr_compressed_ptr recovery_map) {
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map) {
if (compressed_jpegr_image == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
index 1617b8b..40956bd 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -93,10 +93,8 @@
string val;
if (gContainerItemState == Started) {
if (context.BuildTokenValue(&val)) {
- if (!val.compare(rangeScalingFactorAttrName)) {
- lastAttributeName = rangeScalingFactorAttrName;
- } else if (!val.compare(transferFunctionAttrName)) {
- lastAttributeName = transferFunctionAttrName;
+ if (!val.compare(maxContentBoostAttrName)) {
+ lastAttributeName = maxContentBoostAttrName;
} else {
lastAttributeName = "";
}
@@ -109,22 +107,20 @@
string val;
if (gContainerItemState == Started) {
if (context.BuildTokenValue(&val, true)) {
- if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
- rangeScalingFactorStr = val;
- } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
- transferFunctionStr = val;
+ if (!lastAttributeName.compare(maxContentBoostAttrName)) {
+ maxContentBoostStr = val;
}
}
}
return context.GetResult();
}
- bool getRangeScalingFactor(float* scaling_factor) {
+ bool getMaxContentBoost(float* max_content_boost) {
if (gContainerItemState == Done) {
- stringstream ss(rangeScalingFactorStr);
+ stringstream ss(maxContentBoostStr);
float val;
if (ss >> val) {
- *scaling_factor = val;
+ *max_content_boost = val;
return true;
} else {
return false;
@@ -134,84 +130,49 @@
}
}
- bool getTransferFunction(jpegr_transfer_function* transfer_function) {
- if (gContainerItemState == Done) {
- stringstream ss(transferFunctionStr);
- int val;
- if (ss >> val) {
- *transfer_function = static_cast<jpegr_transfer_function>(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- return true;
- }
-
private:
static const string gContainerItemName;
- static const string rangeScalingFactorAttrName;
- static const string transferFunctionAttrName;
- string rangeScalingFactorStr;
- string transferFunctionStr;
+ static const string maxContentBoostAttrName;
+ string maxContentBoostStr;
string lastAttributeName;
ParseState gContainerItemState;
};
// GContainer XMP constants - URI and namespace prefix
const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
-const string kContainerPrefix = "GContainer";
+const string kContainerPrefix = "Container";
// GContainer XMP constants - element and attribute names
const string kConDirectory = Name(kContainerPrefix, "Directory");
const string kConItem = Name(kContainerPrefix, "Item");
-const string kConItemLength = Name(kContainerPrefix, "ItemLength");
-const string kConItemMime = Name(kContainerPrefix, "ItemMime");
-const string kConItemSemantic = Name(kContainerPrefix, "ItemSemantic");
-const string kConVersion = Name(kContainerPrefix, "Version");
-
-// GContainer XMP constants - element and attribute values
-const string kSemanticPrimary = "Primary";
-const string kSemanticRecoveryMap = "RecoveryMap";
-const string kMimeImageJpeg = "image/jpeg";
-
-const int kGContainerVersion = 1;
// GContainer XMP constants - names for XMP handlers
const string XMPXmlHandler::gContainerItemName = kConItem;
+// Item XMP constants - URI and namespace prefix
+const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
+const string kItemPrefix = "Item";
+
+// Item XMP constants - element and attribute names
+const string kItemLength = Name(kItemPrefix, "Length");
+const string kItemMime = Name(kItemPrefix, "Mime");
+const string kItemSemantic = Name(kItemPrefix, "Semantic");
+
+// Item XMP constants - element and attribute values
+const string kSemanticPrimary = "Primary";
+const string kSemanticRecoveryMap = "RecoveryMap";
+const string kMimeImageJpeg = "image/jpeg";
+
// RecoveryMap XMP constants - URI and namespace prefix
const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
const string kRecoveryMapPrefix = "RecoveryMap";
// RecoveryMap XMP constants - element and attribute names
-const string kMapRangeScalingFactor = Name(kRecoveryMapPrefix, "RangeScalingFactor");
-const string kMapTransferFunction = Name(kRecoveryMapPrefix, "TransferFunction");
-const string kMapVersion = Name(kRecoveryMapPrefix, "Version");
-
-const string kMapHdr10Metadata = Name(kRecoveryMapPrefix, "HDR10Metadata");
-const string kMapHdr10MaxFall = Name(kRecoveryMapPrefix, "HDR10MaxFALL");
-const string kMapHdr10MaxCll = Name(kRecoveryMapPrefix, "HDR10MaxCLL");
-
-const string kMapSt2086Metadata = Name(kRecoveryMapPrefix, "ST2086Metadata");
-const string kMapSt2086MaxLum = Name(kRecoveryMapPrefix, "ST2086MaxLuminance");
-const string kMapSt2086MinLum = Name(kRecoveryMapPrefix, "ST2086MinLuminance");
-const string kMapSt2086Primary = Name(kRecoveryMapPrefix, "ST2086Primary");
-const string kMapSt2086Coordinate = Name(kRecoveryMapPrefix, "ST2086Coordinate");
-const string kMapSt2086CoordinateX = Name(kRecoveryMapPrefix, "ST2086CoordinateX");
-const string kMapSt2086CoordinateY = Name(kRecoveryMapPrefix, "ST2086CoordinateY");
-
-// RecoveryMap XMP constants - element and attribute values
-const int kSt2086PrimaryRed = 0;
-const int kSt2086PrimaryGreen = 1;
-const int kSt2086PrimaryBlue = 2;
-const int kSt2086PrimaryWhite = 3;
+const string kMapMaxContentBoost = Name(kRecoveryMapPrefix, "MaxContentBoost");
+const string kMapVersion = Name(kRecoveryMapPrefix, "Version");
// RecoveryMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::rangeScalingFactorAttrName = kMapRangeScalingFactor;
-const string XMPXmlHandler::transferFunctionAttrName = kMapTransferFunction;
+const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -248,13 +209,10 @@
return false;
}
- if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
+ if (!handler.getMaxContentBoost(&metadata->maxContentBoost)) {
return false;
}
- if (!handler.getTransferFunction(&metadata->transferFunction)) {
- return false;
- }
return true;
}
@@ -271,66 +229,19 @@
writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
writer.StartWritingElement("rdf:Description");
writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteXmlns(kItemPrefix, kItemUri);
writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
- writer.WriteElementAndContent(kConVersion, kGContainerVersion);
writer.StartWritingElements(kConDirSeq);
size_t item_depth = writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticPrimary);
- writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
- writer.WriteAttributeNameAndValue(kMapRangeScalingFactor, metadata.rangeScalingFactor);
- writer.WriteAttributeNameAndValue(kMapTransferFunction, metadata.transferFunction);
- if (metadata.transferFunction == JPEGR_TF_PQ) {
- writer.StartWritingElement(kMapHdr10Metadata);
- writer.WriteAttributeNameAndValue(kMapHdr10MaxFall, metadata.hdr10Metadata.maxFALL);
- writer.WriteAttributeNameAndValue(kMapHdr10MaxCll, metadata.hdr10Metadata.maxCLL);
- writer.StartWritingElement(kMapSt2086Metadata);
- writer.WriteAttributeNameAndValue(
- kMapSt2086MaxLum, metadata.hdr10Metadata.st2086Metadata.maxLuminance);
- writer.WriteAttributeNameAndValue(
- kMapSt2086MinLum, metadata.hdr10Metadata.st2086Metadata.minLuminance);
-
- // red
- writer.StartWritingElement(kMapSt2086Coordinate);
- writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryRed);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
- writer.FinishWritingElement();
-
- // green
- writer.StartWritingElement(kMapSt2086Coordinate);
- writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryGreen);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
- writer.FinishWritingElement();
-
- // blue
- writer.StartWritingElement(kMapSt2086Coordinate);
- writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryBlue);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
- writer.FinishWritingElement();
-
- // white
- writer.StartWritingElement(kMapSt2086Coordinate);
- writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryWhite);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
- writer.WriteAttributeNameAndValue(
- kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
- writer.FinishWritingElement();
- }
+ writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
writer.FinishWritingElementsToDepth(item_depth);
writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticRecoveryMap);
- writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
- writer.WriteAttributeNameAndValue(kConItemLength, secondary_image_length);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
writer.FinishWriting();
return ss.str();
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index cad273e..e381caf 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -29,14 +29,17 @@
"recoverymapmath_test.cpp",
],
shared_libs: [
- "libjpeg",
- "libjpegrecoverymap",
"libimage_io",
+ "libjpeg",
"liblog",
],
static_libs: [
"libgmock",
"libgtest",
+ "libjpegdecoder",
+ "libjpegencoder",
+ "libjpegrecoverymap",
+ "libskia",
],
}
@@ -48,11 +51,11 @@
],
shared_libs: [
"libjpeg",
- "libjpegencoder",
"liblog",
],
static_libs: [
"libgtest",
+ "libjpegencoder",
],
}
@@ -64,10 +67,10 @@
],
shared_libs: [
"libjpeg",
- "libjpegdecoder",
"liblog",
],
static_libs: [
"libgtest",
+ "libjpegdecoder",
],
}
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index dfab76a..3e9a76d 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -103,8 +103,7 @@
TEST_F(RecoveryMapTest, writeXmpThenRead) {
jpegr_metadata metadata_expected;
- metadata_expected.transferFunction = JPEGR_TF_HLG;
- metadata_expected.rangeScalingFactor = 1.25;
+ metadata_expected.maxContentBoost = 1.25;
int length_expected = 1000;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
@@ -120,8 +119,7 @@
jpegr_metadata metadata_read;
EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
- ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
- ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
+ ASSERT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
}
/* Test Encode API-0 and decode */
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 1d522d1..2eec95f 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -88,10 +88,10 @@
return luminance_scaled * scale_factor;
}
- Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+ Color Recover(Color yuv_gamma, float recovery, float max_content_boost) {
Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
Color rgb = srgbInvOetf(rgb_gamma);
- return applyRecovery(rgb, recovery, range_scaling_factor);
+ return applyRecovery(rgb, recovery, max_content_boost);
}
jpegr_uncompressed_struct Yuv420Image() {
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index cacf30b..2a3dba7 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -42,9 +42,11 @@
"options": [
{
"include-filter": "android.view.cts.input",
+ "include-filter": "android.view.cts.HoverTest",
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
"include-filter": "android.view.cts.TooltipTest",
+ "include-filter": "android.view.cts.VelocityTrackerTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
@@ -131,9 +133,12 @@
"name": "CtsViewTestCases",
"options": [
{
+ "include-filter": "android.view.cts.input",
+ "include-filter": "android.view.cts.HoverTest",
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
"include-filter": "android.view.cts.TooltipTest",
+ "include-filter": "android.view.cts.VelocityTrackerTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index 512cb6e..48f9f2b 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -16,7 +16,8 @@
#pragma once
-#include <utils/BitSet.h>
+#include <input/Input.h>
+#include <bitset>
#include <optional>
namespace android {
@@ -47,7 +48,7 @@
std::optional<int32_t> displayId = std::nullopt;
// The specific pointers to cancel, or nullopt to cancel all pointer events
- std::optional<BitSet32> pointerIds = std::nullopt;
+ std::optional<std::bitset<MAX_POINTER_ID + 1>> pointerIds = std::nullopt;
CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {}
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 143d25c..079b80d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -190,7 +190,7 @@
pointerCount, MAX_POINTERS);
return false;
}
- BitSet32 pointerIdBits;
+ std::bitset<MAX_POINTER_ID + 1> pointerIdBits;
for (size_t i = 0; i < pointerCount; i++) {
int32_t id = pointerProperties[i].id;
if (id < 0 || id > MAX_POINTER_ID) {
@@ -198,11 +198,11 @@
MAX_POINTER_ID);
return false;
}
- if (pointerIdBits.hasBit(id)) {
+ if (pointerIdBits.test(id)) {
ALOGE("Motion event has duplicate pointer id %d", id);
return false;
}
- pointerIdBits.markBit(id);
+ pointerIdBits.set(id);
}
return true;
}
@@ -292,6 +292,17 @@
first->applicationInfo.token == second->applicationInfo.token;
}
+template <typename T>
+size_t firstMarkedBit(T set) {
+ // TODO: replace with std::countr_zero from <bit> when that's available
+ LOG_ALWAYS_FATAL_IF(set.none());
+ size_t i = 0;
+ while (!set.test(i)) {
+ i++;
+ }
+ return i;
+}
+
std::unique_ptr<DispatchEntry> createDispatchEntry(
const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags) {
@@ -312,7 +323,7 @@
// as long as all other pointers are normalized to the same value and the final DispatchEntry
// uses the transform for the normalized pointer.
const ui::Transform& firstPointerTransform =
- inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()];
+ inputTarget.pointerTransforms[firstMarkedBit(inputTarget.pointerIds)];
ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
// Iterate through all pointers in the event to normalize against the first.
@@ -591,7 +602,7 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = oldWindow;
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_EXIT;
- touchedWindow.pointerIds.markBit(pointerId);
+ touchedWindow.pointerIds.set(pointerId);
out.push_back(touchedWindow);
}
}
@@ -608,7 +619,7 @@
LOG_ALWAYS_FATAL_IF(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE);
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
}
- touchedWindow.pointerIds.markBit(pointerId);
+ touchedWindow.pointerIds.set(pointerId);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -1165,7 +1176,7 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
- BitSet32(0), /*firstDownTimeInTarget=*/std::nullopt,
+ /*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
outsideTargets);
}
}
@@ -1662,7 +1673,7 @@
std::vector<InputTarget> inputTargets;
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS,
- BitSet32(0), getDownTime(*entry), inputTargets);
+ /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1771,7 +1782,7 @@
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND |
InputTarget::Flags::DISPATCH_AS_IS,
- BitSet32(0), getDownTime(*entry), inputTargets);
+ /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
}
}
if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -2126,7 +2137,7 @@
// Eventually, touchedWindow will contain the deviceId of each pointer that's currently
// being sent there. For now, use deviceId from touch state.
- if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) {
+ if (entry.deviceId == touchState.deviceId && touchedWindow.pointerIds.any()) {
return false;
}
}
@@ -2297,9 +2308,9 @@
}
// Update the temporary touch state.
- BitSet32 pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
if (!isHoverAction) {
- pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+ pointerIds.set(entry.pointerProperties[pointerIndex].id);
}
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
@@ -2341,7 +2352,7 @@
// which is a specific behaviour that we want.
const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.hasBit(pointerId) &&
+ if (touchedWindow.pointerIds.test(pointerId) &&
touchedWindow.pilferedPointerIds.count() > 0) {
// This window is already pilfering some pointers, and this new pointer is also
// going to it. Therefore, take over this pointer and don't give it to anyone
@@ -2397,9 +2408,9 @@
newTouchedWindowHandle->getName().c_str(), displayId);
}
// Make a slippery exit from the old window.
- BitSet32 pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
const int32_t pointerId = entry.pointerProperties[0].id;
- pointerIds.markBit(pointerId);
+ pointerIds.set(pointerId);
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
@@ -2446,7 +2457,7 @@
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+ touchedWindow.pointerIds.set(entry.pointerProperties[pointerIndex].id);
}
}
}
@@ -2520,8 +2531,7 @@
// Success! Output targets from the touch state.
tempTouchState.clearWindowsWithoutPointers();
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.isEmpty() &&
- !touchedWindow.hasHoveringPointers(entry.deviceId)) {
+ if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
// Do not send this event to those windows.
continue;
@@ -2576,8 +2586,8 @@
for (size_t i = 0; i < tempTouchState.windows.size();) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
+ touchedWindow.pointerIds.reset(pointerId);
+ if (touchedWindow.pointerIds.none()) {
tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
}
@@ -2702,7 +2712,7 @@
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- BitSet32 pointerIds,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const {
std::vector<InputTarget>::iterator it =
@@ -3012,9 +3022,9 @@
}
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
- "globalScaleFactor=%f, pointerIds=0x%x %s",
+ "globalScaleFactor=%f, pointerIds=%s %s",
connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
- inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+ inputTarget.globalScaleFactor, bitsetToString(inputTarget.pointerIds).c_str(),
inputTarget.getPointerInfoString().c_str());
}
@@ -3887,8 +3897,9 @@
}
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
- const MotionEntry& originalMotionEntry, BitSet32 pointerIds, nsecs_t splitDownTime) {
- ALOG_ASSERT(pointerIds.value != 0);
+ const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ nsecs_t splitDownTime) {
+ ALOG_ASSERT(pointerIds.any());
uint32_t splitPointerIndexMap[MAX_POINTERS];
PointerProperties splitPointerProperties[MAX_POINTERS];
@@ -3902,7 +3913,7 @@
const PointerProperties& pointerProperties =
originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.hasBit(pointerId)) {
+ if (pointerIds.test(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
splitPointerCoords[splitPointerCount].copyFrom(
@@ -3918,7 +3929,7 @@
// or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
// in this way.
ALOGW("Dropping split motion event because the pointer count is %d but "
- "we expected there to be %d pointers. This probably means we received "
+ "we expected there to be %zu pointers. This probably means we received "
"a broken sequence of pointer ids from the input device.",
splitPointerCount, pointerIds.count());
return nullptr;
@@ -3932,7 +3943,7 @@
const PointerProperties& pointerProperties =
originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.hasBit(pointerId)) {
+ if (pointerIds.test(pointerId)) {
if (pointerIds.count() == 1) {
// The first/last pointer went down/up.
action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
@@ -5243,7 +5254,7 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- BitSet32 pointerIds = touchedWindow->pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->pointerIds;
sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
@@ -5264,7 +5275,7 @@
return false;
}
// Track the pointer id for drag window and generate the drag state.
- const int32_t id = pointerIds.firstMarkedBit();
+ const size_t id = firstMarkedBit(pointerIds);
mDragState = std::make_unique<DragState>(toWindowHandle, id);
}
@@ -5764,7 +5775,7 @@
}
auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
- if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.isEmpty()) {
+ if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.none()) {
ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
@@ -5794,10 +5805,7 @@
// Prevent the gesture from being sent to any other windows.
// This only blocks relevant pointers to be sent to other windows
- for (BitSet32 idBits(window.pointerIds); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- window.pilferedPointerIds.set(id);
- }
+ window.pilferedPointerIds |= window.pointerIds;
state.cancelPointersForWindowsExcept(window.pointerIds, token);
return OK;
@@ -6566,8 +6574,8 @@
const sp<WindowInfoHandle>& newWindowHandle,
TouchState& state, int32_t pointerId,
std::vector<InputTarget>& targets) {
- BitSet32 pointerIds;
- pointerIds.markBit(pointerId);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(pointerId);
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -6603,7 +6611,8 @@
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<WindowInfoHandle> fromWindowHandle,
const sp<WindowInfoHandle> toWindowHandle,
- TouchState& state, const BitSet32& pointerIds) {
+ TouchState& state,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 81f8de8..b94858b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -46,6 +46,7 @@
#include <utils/Looper.h>
#include <utils/Timers.h>
#include <utils/threads.h>
+#include <bitset>
#include <condition_variable>
#include <deque>
#include <optional>
@@ -550,7 +551,8 @@
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
@@ -636,7 +638,8 @@
// Splitting motion events across windows. When splitting motion event for a target,
// splitDownTime refers to the time of first 'down' event on that particular target
std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
- BitSet32 pointerIds, nsecs_t splitDownTime);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ nsecs_t splitDownTime);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -703,7 +706,8 @@
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
- TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+ TouchState& state, std::bitset<MAX_POINTER_ID + 1> pointerIds)
+ REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index de8bfd5..ad5a7fd 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -374,7 +374,8 @@
}
std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEventsForPointers(
- const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime) {
+ const MotionMemento& memento, std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ nsecs_t currentTime) {
std::vector<std::unique_ptr<MotionEntry>> events;
std::vector<uint32_t> canceledPointerIndices;
std::vector<PointerProperties> pointerProperties(MAX_POINTERS);
@@ -383,7 +384,7 @@
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]);
pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]);
- if (pointerIds.hasBit(pointerId)) {
+ if (pointerIds.test(pointerId)) {
canceledPointerIndices.push_back(pointerIdx);
}
}
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 6ab48c9..42d8cc6 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -20,6 +20,7 @@
#include "Entry.h"
#include <utils/Timers.h>
+#include <bitset>
namespace android {
namespace inputdispatcher {
@@ -128,7 +129,8 @@
// Synthesizes pointer cancel events for a particular set of pointers.
std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
- const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime);
+ const MotionMemento& memento, std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ nsecs_t currentTime);
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 2f39480..fc8b785 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -24,31 +24,34 @@
namespace android::inputdispatcher {
-void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
+void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
+ const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
// valid pointer property from the input event.
- if (newPointerIds.isEmpty()) {
+ if (newPointerIds.none()) {
setDefaultPointerTransform(transform);
return;
}
// Ensure that the new set of pointers doesn't overlap with the current set of pointers.
- ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+ LOG_ALWAYS_FATAL_IF((pointerIds & newPointerIds).any());
pointerIds |= newPointerIds;
- while (!newPointerIds.isEmpty()) {
- int32_t pointerId = newPointerIds.clearFirstMarkedBit();
- pointerTransforms[pointerId] = transform;
+ for (size_t i = 0; i < newPointerIds.size(); i++) {
+ if (!newPointerIds.test(i)) {
+ continue;
+ }
+ pointerTransforms[i] = transform;
}
}
void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
- pointerIds.clear();
+ pointerIds.reset();
pointerTransforms[0] = transform;
}
bool InputTarget::useDefaultPointerTransform() const {
- return pointerIds.isEmpty();
+ return pointerIds.none();
}
const ui::Transform& InputTarget::getDefaultPointerTransform() const {
@@ -63,8 +66,8 @@
return out;
}
- for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
- if (!pointerIds.hasBit(i)) {
+ for (uint32_t i = 0; i < pointerIds.size(); i++) {
+ if (!pointerIds.test(i)) {
continue;
}
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 61b07fe..7b12f81 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -21,6 +21,7 @@
#include <input/InputTransport.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
+#include <bitset>
namespace android::inputdispatcher {
@@ -105,7 +106,7 @@
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
- BitSet32 pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
// Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
// FLAG_SPLIT is set.
std::optional<nsecs_t> firstDownTimeInTarget;
@@ -113,7 +114,7 @@
// Transform per pointerId.
ui::Transform pointerTransforms[MAX_POINTERS];
- void addPointers(BitSet32 pointerIds, const ui::Transform& transform);
+ void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform);
void setDefaultPointerTransform(const ui::Transform& transform);
/**
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index c257ee5..acfd0a2 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -33,7 +33,7 @@
void TouchState::removeTouchedPointer(int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.pointerIds.clearBit(pointerId);
+ touchedWindow.pointerIds.reset(pointerId);
touchedWindow.pilferedPointerIds.reset(pointerId);
}
}
@@ -42,7 +42,7 @@
int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.pointerIds.clearBit(pointerId);
+ touchedWindow.pointerIds.reset(pointerId);
touchedWindow.pilferedPointerIds.reset(pointerId);
return;
}
@@ -57,12 +57,13 @@
void TouchState::clearWindowsWithoutPointers() {
std::erase_if(windows, [](const TouchedWindow& w) {
- return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
+ return w.pointerIds.none() && !w.hasHoveringPointers();
});
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
@@ -75,7 +76,7 @@
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when an pointer is down for
// the window.
- touchedWindow.pointerIds.value |= pointerIds.value;
+ touchedWindow.pointerIds |= pointerIds;
if (!touchedWindow.firstDownTimeInTarget.has_value()) {
touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
}
@@ -128,15 +129,15 @@
}
}
-void TouchState::cancelPointersForWindowsExcept(const BitSet32 pointerIds,
+void TouchState::cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
- if (pointerIds.isEmpty()) return;
+ if (pointerIds.none()) return;
std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) {
if (w.windowHandle->getToken() != token) {
- w.pointerIds &= BitSet32(~pointerIds.value);
+ w.pointerIds &= ~pointerIds;
}
});
- std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
+ std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
}
/**
@@ -147,7 +148,7 @@
*/
void TouchState::cancelPointersForNonPilferingWindows() {
// First, find all pointers that are being pilfered, across all windows
- std::bitset<MAX_POINTERS> allPilferedPointerIds;
+ std::bitset<MAX_POINTER_ID + 1> allPilferedPointerIds;
std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](const TouchedWindow& w) {
allPilferedPointerIds |= w.pilferedPointerIds;
});
@@ -161,7 +162,7 @@
// pilfered pointers will be disjoint across all windows, but there's no reason to cause that
// limitation here.
std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](TouchedWindow& w) {
- std::bitset<MAX_POINTERS> pilferedByOtherWindows =
+ std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
w.pilferedPointerIds ^ allPilferedPointerIds;
// TODO(b/211379801) : convert pointerIds to use std::bitset, which would allow us to
// replace the loop below with a bitwise operation. Currently, the XOR operation above is
@@ -171,11 +172,11 @@
// Pointer is pilfered by other windows, but not by this one! Remove it from here.
// We could call 'removeTouchedPointerFromWindow' here, but it's faster to directly
// manipulate it.
- w.pointerIds.clearBit(i);
+ w.pointerIds.reset(i);
}
}
});
- std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
+ std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
}
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -224,7 +225,7 @@
bool TouchState::isDown() const {
return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
+ [](const TouchedWindow& window) { return window.pointerIds.any(); });
}
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
@@ -243,7 +244,7 @@
window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
}
std::erase_if(windows, [](const TouchedWindow& w) {
- return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
+ return w.pointerIds.none() && !w.hasHoveringPointers();
});
}
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index f1409d6..6e965d8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -16,6 +16,7 @@
#pragma once
+#include <bitset>
#include <set>
#include "TouchedWindow.h"
@@ -46,7 +47,8 @@
void removeTouchedPointerFromWindow(int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t deviceId, int32_t hoveringPointerId);
@@ -56,7 +58,8 @@
void filterNonAsIsTouchWindows();
// Cancel pointers for current set of windows except the window with particular binder token.
- void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);
+ void cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ const sp<IBinder>& token);
// Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
// set to false.
void cancelPointersForNonPilferingWindows();
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 99e1c86..92f62b5 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -66,9 +66,9 @@
std::string out;
std::string hoveringPointers =
dumpMap(mHoveringPointerIdsByDevice, constToString, bitsetToString);
- out += StringPrintf("name='%s', pointerIds=0x%0x, targetFlags=%s, firstDownTimeInTarget=%s, "
+ out += StringPrintf("name='%s', pointerIds=%s, targetFlags=%s, firstDownTimeInTarget=%s, "
"mHoveringPointerIdsByDevice=%s, pilferedPointerIds=%s\n",
- windowHandle->getName().c_str(), pointerIds.value,
+ windowHandle->getName().c_str(), bitsetToString(pointerIds).c_str(),
targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str(),
hoveringPointers.c_str(), bitsetToString(pilferedPointerIds).c_str());
return out;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4ec33ac..e59e781 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -30,9 +30,9 @@
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
ftl::Flags<InputTarget::Flags> targetFlags;
- BitSet32 pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
// The pointer ids of the pointers that this window is currently pilfering
- std::bitset<MAX_POINTERS> pilferedPointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pilferedPointerIds;
// Time at which the first action down occurred on this window.
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
std::optional<nsecs_t> firstDownTimeInTarget;
@@ -47,7 +47,7 @@
std::string dump() const;
private:
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTERS>> mHoveringPointerIdsByDevice;
+ std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mHoveringPointerIdsByDevice;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 2173117..841c914 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -333,6 +333,9 @@
// stylus button state changes are reported through motion events.
bool stylusButtonMotionEventsEnabled;
+ // True if a pointer icon should be shown for direct stylus pointers.
+ bool stylusPointerIconEnabled;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
@@ -358,7 +361,8 @@
touchpadNaturalScrollingEnabled(true),
touchpadTapToClickEnabled(true),
touchpadRightClickZoneEnabled(false),
- stylusButtonMotionEventsEnabled(true) {}
+ stylusButtonMotionEventsEnabled(true),
+ stylusPointerIconEnabled(false) {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 7e0c1c7..9dbdd5a 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -79,8 +79,10 @@
POINTER,
// Show spots and a spot anchor in place of the mouse pointer.
SPOT,
+ // Show the stylus hover pointer.
+ STYLUS_HOVER,
- ftl_last = SPOT,
+ ftl_last = STYLUS_HOVER,
};
/* Sets the mode of the pointer controller. */
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d415854..b789156 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -977,12 +977,18 @@
mOrientedRanges.clear();
}
- // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to
- // preserve the cursor position.
- if (mDeviceMode == DeviceMode::POINTER ||
- (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
- (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerCaptureRequest.enable)) {
+ // Create and preserve the pointer controller in the following cases:
+ const bool isPointerControllerNeeded =
+ // - when the device is in pointer mode, to show the mouse cursor;
+ (mDeviceMode == DeviceMode::POINTER) ||
+ // - when pointer capture is enabled, to preserve the mouse cursor position;
+ (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+ mConfig.pointerCaptureRequest.enable) ||
+ // - when we should be showing touches;
+ (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
+ // - when we should be showing a pointer icon for direct styluses.
+ (mDeviceMode == DeviceMode::DIRECT && mConfig.stylusPointerIconEnabled && hasStylus());
+ if (isPointerControllerNeeded) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
@@ -3650,6 +3656,14 @@
return out;
}
+static bool isStylusEvent(uint32_t source, int32_t action, const PointerProperties* properties) {
+ if (!isFromSource(source, AINPUT_SOURCE_STYLUS)) {
+ return false;
+ }
+ const auto actionIndex = action >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ return isStylusToolType(properties[actionIndex].toolType);
+}
+
NotifyMotionArgs TouchInputMapper::dispatchMotion(
nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
@@ -3691,12 +3705,35 @@
ALOG_ASSERT(false);
}
}
+
+ const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
+ const bool showDirectStylusPointer = mConfig.stylusPointerIconEnabled &&
+ mDeviceMode == DeviceMode::DIRECT && isStylusEvent(source, action, pointerProperties) &&
+ mPointerController && displayId != ADISPLAY_ID_NONE &&
+ displayId == mPointerController->getDisplayId();
+ if (showDirectStylusPointer) {
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ mPointerController->setPresentation(
+ PointerControllerInterface::Presentation::STYLUS_HOVER);
+ mPointerController
+ ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[0].getX(),
+ mCurrentCookedState.cookedPointerData.pointerCoords[0]
+ .getY());
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ break;
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ break;
+ }
+ }
+
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
- const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index bb8a30e..30c1719 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -205,6 +205,10 @@
mConfig.stylusButtonMotionEventsEnabled = enabled;
}
+void FakeInputReaderPolicy::setStylusPointerIconEnabled(bool enabled) {
+ mConfig.stylusPointerIconEnabled = enabled;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 9ec3217..28ac505 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -76,6 +76,7 @@
float getPointerGestureZoomSpeedRatio();
void setVelocityControlParams(const VelocityControlParameters& params);
void setStylusButtonMotionEventsEnabled(bool enabled);
+ void setStylusPointerIconEnabled(bool enabled);
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index ab7879f..28dad95 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -65,6 +65,10 @@
ASSERT_NEAR(y, actualY, 1);
}
+bool FakePointerController::isPointerShown() {
+ return mIsPointerShown;
+}
+
bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
float* outMaxY) const {
*outMinX = mMinX;
@@ -83,6 +87,13 @@
if (mY > mMaxY) mY = mMaxY;
}
+void FakePointerController::fade(Transition) {
+ mIsPointerShown = false;
+}
+void FakePointerController::unfade(Transition) {
+ mIsPointerShown = true;
+}
+
void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
int32_t displayId) {
std::vector<int32_t> newSpots;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index d10cbcd..dd56e65 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -39,12 +39,13 @@
void setDisplayViewport(const DisplayViewport& viewport) override;
void assertPosition(float x, float y);
+ bool isPointerShown();
private:
bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
void move(float deltaX, float deltaY) override;
- void fade(Transition) override {}
- void unfade(Transition) override {}
+ void fade(Transition) override;
+ void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
int32_t displayId) override;
@@ -55,6 +56,7 @@
float mX{0}, mY{0};
int32_t mButtonState{0};
int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ bool mIsPointerShown{false};
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e1c54e9..1b04375 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6678,6 +6678,52 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
}
+TEST_F(SingleTouchInputMapperTest, Process_WhenConfigEnabled_ShouldShowDirectStylusPointer) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(fakePointerController);
+ mFakePolicy->setStylusPointerIconEnabled(true);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processMove(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
+ ASSERT_TRUE(fakePointerController->isPointerShown());
+ ASSERT_NO_FATAL_FAILURE(
+ fakePointerController->assertPosition(toDisplayX(100), toDisplayY(200)));
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirectStylusPointer) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(fakePointerController);
+ mFakePolicy->setStylusPointerIconEnabled(false);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processMove(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
+ ASSERT_FALSE(fakePointerController->isPointerShown());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -9757,6 +9803,58 @@
WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
}
+TEST_F(MultiTouchInputMapperTest, Process_WhenConfigEnabled_ShouldShowDirectStylusPointer) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE | PRESSURE);
+ // Add BTN_TOOL_PEN to statically show stylus support, since using ABS_MT_TOOL_TYPE can only
+ // indicate stylus presence dynamically.
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(fakePointerController);
+ mFakePolicy->setStylusPointerIconEnabled(true);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPressure(mapper, RAW_PRESSURE_MIN);
+ processPosition(mapper, 100, 200);
+ processToolType(mapper, MT_TOOL_PEN);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
+ ASSERT_TRUE(fakePointerController->isPointerShown());
+ ASSERT_NO_FATAL_FAILURE(
+ fakePointerController->assertPosition(toDisplayX(100), toDisplayY(200)));
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirectStylusPointer) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE | PRESSURE);
+ // Add BTN_TOOL_PEN to statically show stylus support, since using ABS_MT_TOOL_TYPE can only
+ // indicate stylus presence dynamically.
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(fakePointerController);
+ mFakePolicy->setStylusPointerIconEnabled(false);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPressure(mapper, RAW_PRESSURE_MIN);
+ processPosition(mapper, 100, 200);
+ processToolType(mapper, MT_TOOL_PEN);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
+ ASSERT_FALSE(fakePointerController->isPointerShown());
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/stats/.clang-format b/services/stats/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/services/stats/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 410a5af..1348548 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -14,32 +14,33 @@
* limitations under the License.
*/
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#define LOG_TAG "StatsAidl"
+#include "StatsAidl.h"
+
#include <log/log.h>
#include <statslog.h>
-#include "StatsAidl.h"
-
namespace aidl {
namespace android {
namespace frameworks {
namespace stats {
-StatsHal::StatsHal() {}
+StatsHal::StatsHal() {
+}
ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
- ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+ ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId);
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
- -1, "Not a valid vendor atom ID");
+ -1, "Not a valid vendor atom ID");
}
if (vendorAtom.reverseDomainName.length() > 50) {
ALOGE("Vendor atom reverse domain name %s is too long.",
- vendorAtom.reverseDomainName.c_str());
+ vendorAtom.reverseDomainName.c_str());
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
- -1, "Vendor atom reverse domain name is too long");
+ -1, "Vendor atom reverse domain name is too long");
}
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, vendorAtom.atomId);
@@ -47,24 +48,20 @@
for (const auto& atomValue : vendorAtom.values) {
switch (atomValue.getTag()) {
case VendorAtomValue::intValue:
- AStatsEvent_writeInt32(event,
- atomValue.get<VendorAtomValue::intValue>());
+ AStatsEvent_writeInt32(event, atomValue.get<VendorAtomValue::intValue>());
break;
case VendorAtomValue::longValue:
- AStatsEvent_writeInt64(event,
- atomValue.get<VendorAtomValue::longValue>());
+ AStatsEvent_writeInt64(event, atomValue.get<VendorAtomValue::longValue>());
break;
case VendorAtomValue::floatValue:
- AStatsEvent_writeFloat(event,
- atomValue.get<VendorAtomValue::floatValue>());
+ AStatsEvent_writeFloat(event, atomValue.get<VendorAtomValue::floatValue>());
break;
case VendorAtomValue::stringValue:
AStatsEvent_writeString(event,
- atomValue.get<VendorAtomValue::stringValue>().c_str());
+ atomValue.get<VendorAtomValue::stringValue>().c_str());
break;
case VendorAtomValue::boolValue:
- AStatsEvent_writeBool(event,
- atomValue.get<VendorAtomValue::boolValue>());
+ AStatsEvent_writeBool(event, atomValue.get<VendorAtomValue::boolValue>());
break;
case VendorAtomValue::repeatedIntValue: {
const std::optional<std::vector<int>>& repeatedIntValue =
@@ -112,8 +109,8 @@
for (int i = 0; i < repeatedStringVector.size(); ++i) {
cStringArray[i] = repeatedStringVector[i].has_value()
- ? repeatedStringVector[i]->c_str()
- : "";
+ ? repeatedStringVector[i]->c_str()
+ : "";
}
AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
@@ -152,9 +149,9 @@
const int ret = AStatsEvent_write(event);
AStatsEvent_release(event);
- return ret <= 0 ?
- ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") :
- ndk::ScopedAStatus::ok();
+ return ret <= 0 ? ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret,
+ "report atom failed")
+ : ndk::ScopedAStatus::ok();
}
} // namespace stats
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index d27d989..19176d9 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -14,42 +14,42 @@
* limitations under the License.
*/
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#define LOG_TAG "StatsHal"
+#include "StatsHal.h"
+
#include <log/log.h>
#include <statslog.h>
-#include "StatsHal.h"
-
namespace android {
namespace frameworks {
namespace stats {
namespace V1_0 {
namespace implementation {
-StatsHal::StatsHal() {}
+StatsHal::StatsHal() {
+}
-hardware::Return<void> StatsHal::reportSpeakerImpedance(
- const SpeakerImpedance& speakerImpedance) {
+hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) {
android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
- speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+ speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
- hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+ hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
return hardware::Void();
}
hardware::Return<void> StatsHal::reportPhysicalDropDetected(
const PhysicalDropDetected& physicalDropDetected) {
- android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
- int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
- physicalDropDetected.freefallDuration);
+ android::util::stats_write(
+ android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg),
+ physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration);
return hardware::Void();
}
@@ -58,20 +58,21 @@
std::vector<int32_t> buckets = chargeCycles.cycleBucket;
int initialSize = buckets.size();
for (int i = 0; i < 10 - initialSize; i++) {
- buckets.push_back(0); // Push 0 for buckets that do not exist.
+ buckets.push_back(0); // Push 0 for buckets that do not exist.
}
android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
- buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
- buckets[9]);
+ buckets[2], buckets[3], buckets[4], buckets[5], buckets[6],
+ buckets[7], buckets[8], buckets[9]);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
- android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
- int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
- batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
+ android::util::stats_write(
+ android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type),
+ batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV,
+ batteryHealthSnapshotArgs.currentMicroA,
batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
@@ -87,14 +88,15 @@
hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
const BatteryCausedShutdown& batteryCausedShutdown) {
android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
- batteryCausedShutdown.voltageMicroV);
+ batteryCausedShutdown.voltageMicroV);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
const UsbPortOverheatEvent& usbPortOverheatEvent) {
- android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+ android::util::stats_write(
+ android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
usbPortOverheatEvent.timeToInactive);
@@ -102,18 +104,17 @@
return hardware::Void();
}
-hardware::Return<void> StatsHal::reportSpeechDspStat(
- const SpeechDspStat& speechDspStat) {
+hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) {
android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
- speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
- speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+ speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
+ speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
- ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+ ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId);
return hardware::Void();
}
if (vendorAtom.reverseDomainName.size() > 50) {
diff --git a/services/stats/include/stats/StatsAidl.h b/services/stats/include/stats/StatsAidl.h
index 219e71e..340b539 100644
--- a/services/stats/include/stats/StatsAidl.h
+++ b/services/stats/include/stats/StatsAidl.h
@@ -28,8 +28,7 @@
/**
* Binder call to get vendor atom.
*/
- virtual ndk::ScopedAStatus reportVendorAtom(
- const VendorAtom& in_vendorAtom) override;
+ virtual ndk::ScopedAStatus reportVendorAtom(const VendorAtom& in_vendorAtom) override;
};
} // namespace stats
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
index 071e54f..864ad14 100644
--- a/services/stats/include/stats/StatsHal.h
+++ b/services/stats/include/stats/StatsHal.h
@@ -16,7 +16,6 @@
#include <android/frameworks/stats/1.0/IStats.h>
#include <android/frameworks/stats/1.0/types.h>
-
#include <stats_event.h>
using namespace android::frameworks::stats::V1_0;
@@ -30,8 +29,8 @@
using android::hardware::Return;
/**
-* Implements the Stats HAL
-*/
+ * Implements the Stats HAL
+ */
class StatsHal : public IStats {
public:
StatsHal();
@@ -50,12 +49,12 @@
* Binder call to get PhysicalDropDetected atom.
*/
virtual Return<void> reportPhysicalDropDetected(
- const PhysicalDropDetected& physicalDropDetected) override;
+ const PhysicalDropDetected& physicalDropDetected) override;
/**
* Binder call to get ChargeCyclesReported atom.
*/
- virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+ virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
/**
* Binder call to get BatteryHealthSnapshot atom.
@@ -83,8 +82,7 @@
/**
* Binder call to get Speech DSP state atom.
*/
- virtual Return<void> reportSpeechDspStat(
- const SpeechDspStat& speechDspStat) override;
+ virtual Return<void> reportSpeechDspStat(const SpeechDspStat& speechDspStat) override;
/**
* Binder call to get vendor atom.
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 6490476..3ed24b2 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -391,7 +391,9 @@
void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
ATRACE_NAME("UpdateSnapshots");
- if (args.forceUpdate || args.displayChanges) {
+ if (args.parentCrop) {
+ mRootSnapshot.geomLayerBounds = *args.parentCrop;
+ } else if (args.forceUpdate || args.displayChanges) {
mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
}
if (args.displayChanges) {
@@ -618,7 +620,8 @@
RequestedLayerState::Changes::AffectsChildren);
snapshot.changes = parentChanges | requested.changes;
snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
- parentSnapshot.invalidTransform || requested.isHiddenByPolicy();
+ parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
+ (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
// TODO(b/238781169) scope down the changes to only buffer updates.
snapshot.hasReadyFrame =
@@ -983,6 +986,20 @@
}
}
+// Visit each visible snapshot in z-order
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const ConstVisitor& visitor,
+ const LayerHierarchy& root) const {
+ root.traverseInZOrder(
+ [this, visitor](const LayerHierarchy&,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ LayerSnapshot* snapshot = getSnapshot(traversalPath);
+ if (snapshot && snapshot->isVisible) {
+ visitor(*snapshot);
+ }
+ return true;
+ });
+}
+
void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) {
for (int i = 0; i < mNumInterestingSnapshots; i++) {
std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index abb7e66..f4544fd 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -36,7 +36,7 @@
class LayerSnapshotBuilder {
public:
struct Args {
- const LayerHierarchy& root;
+ LayerHierarchy root;
const LayerLifecycleManager& layerLifecycleManager;
bool forceUpdate = false;
bool includeMetadata = false;
@@ -46,6 +46,8 @@
const renderengine::ShadowSettings& globalShadowSettings;
bool supportsBlur = true;
bool forceFullDamage = false;
+ std::optional<FloatRect> parentCrop = std::nullopt;
+ std::unordered_set<uint32_t> excludeLayerIds;
};
LayerSnapshotBuilder();
@@ -65,6 +67,9 @@
// Visit each visible snapshot in z-order
void forEachVisibleSnapshot(const ConstVisitor& visitor) const;
+ // Visit each visible snapshot in z-order
+ void forEachVisibleSnapshot(const ConstVisitor& visitor, const LayerHierarchy& root) const;
+
typedef std::function<void(std::unique_ptr<LayerSnapshot>& snapshot)> Visitor;
// Visit each visible snapshot in z-order and move the snapshot if needed
void forEachVisibleSnapshot(const Visitor& visitor);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b6b9965..31ee91e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -146,7 +146,7 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
+ mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
uint32_t layerFlags = 0;
@@ -3098,15 +3098,14 @@
return true;
}
-bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) {
+bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles,
+ bool willPresent) {
// If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
if (handles.empty()) {
mReleasePreviousBuffer = false;
return false;
}
- const bool willPresent = willPresentCurrentTransaction();
-
for (const auto& handle : handles) {
// If this transaction set a buffer on this layer, release its previous buffer
handle->releasePreviousBuffer = mReleasePreviousBuffer;
@@ -3180,11 +3179,10 @@
return fenceSignaled;
}
-bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+void Layer::onPreComposition(nsecs_t refreshStartTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->refreshStartTime = refreshStartTime;
}
- return hasReadyFrame();
}
void Layer::setAutoRefresh(bool autoRefresh) {
@@ -3570,7 +3568,7 @@
sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
// There's no need to get a CE Layer if the layer isn't going to draw anything.
- return hasSomethingToDraw() ? mLayerFE : nullptr;
+ return hasSomethingToDraw() ? mLegacyLayerFE : nullptr;
}
const LayerSnapshot* Layer::getLayerSnapshot() const {
@@ -3581,16 +3579,36 @@
return mSnapshot.get();
}
+std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() {
+ return std::move(mSnapshot);
+}
+
+void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) {
+ mSnapshot = std::move(snapshot);
+}
+
const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
return mSnapshot.get();
}
sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
- auto result = mFlinger->getFactory().createLayerFE(mLayerFE->getDebugName());
+ auto result = mFlinger->getFactory().createLayerFE(mName);
result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
return result;
}
+sp<LayerFE> Layer::getCompositionEngineLayerFE(
+ const frontend::LayerHierarchy::TraversalPath& path) {
+ for (auto& [p, layerFE] : mLayerFEs) {
+ if (p == path) {
+ return layerFE;
+ }
+ }
+ auto layerFE = mFlinger->getFactory().createLayerFE(mName);
+ mLayerFEs.emplace_back(path, layerFE);
+ return layerFE;
+}
+
void Layer::useSurfaceDamage() {
if (mFlinger->mForceFullDamage) {
surfaceDamageRegion = Region::INVALID_REGION;
@@ -3986,28 +4004,6 @@
}
}
-LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
- if (mLayer) {
- mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
- }
-}
-
-LayerSnapshotGuard::~LayerSnapshotGuard() {
- if (mLayer) {
- mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot);
- }
-}
-
-LayerSnapshotGuard::LayerSnapshotGuard(LayerSnapshotGuard&& other) : mLayer(other.mLayer) {
- other.mLayer = nullptr;
-}
-
-LayerSnapshotGuard& LayerSnapshotGuard::operator=(LayerSnapshotGuard&& other) {
- mLayer = other.mLayer;
- other.mLayer = nullptr;
- return *this;
-}
-
void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener) {
bool hadTrustedPresentationListener = hasTrustedPresentationListener();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f858224..3d4f03f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -307,7 +307,8 @@
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
bool setApi(int32_t /*api*/);
bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/);
- bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/);
+ bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
+ bool willPresent);
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
virtual bool setDimmingEnabled(const bool dimmingEnabled);
@@ -328,9 +329,12 @@
virtual sp<LayerFE> getCompositionEngineLayerFE() const;
virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
+ sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
const frontend::LayerSnapshot* getLayerSnapshot() const;
frontend::LayerSnapshot* editLayerSnapshot();
+ std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot();
+ void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot);
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
@@ -512,7 +516,7 @@
// implements compositionengine::LayerFE
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
- bool onPreComposition(nsecs_t refreshStartTime);
+ void onPreComposition(nsecs_t refreshStartTime);
void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
void setWasClientComposed(const sp<Fence>& fence) {
@@ -832,6 +836,7 @@
void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
std::unordered_set<Layer*>& visited);
+ bool willPresentCurrentTransaction() const;
protected:
// For unit tests
@@ -1037,8 +1042,6 @@
// Crop that applies to the buffer
Rect computeBufferCrop(const State& s);
- bool willPresentCurrentTransaction() const;
-
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence,
@@ -1146,34 +1149,10 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<LayerFE> mLayerFE;
+ sp<LayerFE> mLegacyLayerFE;
+ std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
std::unique_ptr<frontend::LayerSnapshot> mSnapshot =
std::make_unique<frontend::LayerSnapshot>();
-
- friend class LayerSnapshotGuard;
-};
-
-// LayerSnapshotGuard manages the movement of LayerSnapshot between a Layer and its corresponding
-// LayerFE. This class must be used whenever LayerFEs are passed to CompositionEngine. Instances of
-// LayerSnapshotGuard should only be constructed on the main thread and should not be moved outside
-// the main thread.
-//
-// Moving the snapshot instead of sharing common state prevents use of LayerFE outside the main
-// thread by making errors obvious (i.e. use outside the main thread results in SEGFAULTs due to
-// nullptr dereference).
-class LayerSnapshotGuard {
-public:
- LayerSnapshotGuard(Layer* layer) REQUIRES(kMainThreadContext);
- ~LayerSnapshotGuard() REQUIRES(kMainThreadContext);
-
- LayerSnapshotGuard(const LayerSnapshotGuard&) = delete;
- LayerSnapshotGuard& operator=(const LayerSnapshotGuard&) = delete;
-
- LayerSnapshotGuard(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
- LayerSnapshotGuard& operator=(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
-
-private:
- Layer* mLayer;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 2b4375b..03a7f22 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -69,6 +69,14 @@
void LayerRenderArea::render(std::function<void()> drawLayers) {
using namespace std::string_literals;
+ if (!mChildrenOnly) {
+ mTransform = mLayer->getTransform().inverse();
+ }
+
+ if (mFlinger.mLayerLifecycleManagerEnabled) {
+ drawLayers();
+ return;
+ }
// If layer is offscreen, update mirroring info if it exists
if (mLayer->isRemovedFromCurrentState()) {
mLayer->traverse(LayerVector::StateSet::Drawing,
@@ -78,7 +86,6 @@
}
if (!mChildrenOnly) {
- mTransform = mLayer->getTransform().inverse();
// If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
// layers in a regular cycles.
if (mLayer->isRemovedFromCurrentState()) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 76e9416..5e79a5c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -532,6 +532,12 @@
const sp<EventThreadConnection>& connection) const {
const auto throttleVsync = [&] {
const auto& vsyncData = event.vsync.vsyncData;
+ if (connection->frameRate.isValid()) {
+ return !mVsyncSchedule->getTracker()
+ .isVSyncInPhase(vsyncData.preferredExpectedPresentationTime(),
+ connection->frameRate);
+ }
+
const auto expectedPresentTime =
TimePoint::fromNs(vsyncData.preferredExpectedPresentationTime());
return !mEventThreadCallback.isVsyncTargetForUid(expectedPresentTime,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index b86553b..aa27091 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -97,6 +97,9 @@
const uid_t mOwnerUid;
const EventRegistrationFlags mEventRegistration;
+ /** The frame rate set to the attached choreographer. */
+ Fps frameRate;
+
private:
virtual void onFirstRef();
EventThread* const mEventThread;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 55fa402..e853833 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -32,6 +32,7 @@
#include <utility>
#include "../Layer.h"
+#include "EventThread.h"
#include "LayerInfo.h"
namespace android::scheduler {
@@ -140,6 +141,22 @@
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
+ // Set frame rate to attached choreographer.
+ // TODO(b/260898223): Change to use layer hierarchy and handle frame rate vote.
+ if (updateType == LayerUpdateType::SetFrameRate) {
+ auto range = mAttachedChoreographers.equal_range(id);
+ auto it = range.first;
+ while (it != range.second) {
+ sp<EventThreadConnection> choreographerConnection = it->second.promote();
+ if (choreographerConnection) {
+ choreographerConnection->frameRate = layer->getFrameRateForLayerTree().rate;
+ it++;
+ } else {
+ it = mAttachedChoreographers.erase(it);
+ }
+ }
+ }
+
// Activate layer if inactive.
if (found == LayerStatus::LayerInInactiveMap) {
mActiveLayerInfos.insert(
@@ -294,6 +311,12 @@
return 0.f;
}
+void LayerHistory::attachChoreographer(int32_t layerId,
+ const sp<EventThreadConnection>& choreographerConnection) {
+ std::lock_guard lock(mLock);
+ mAttachedChoreographers.insert({layerId, wp<EventThreadConnection>(choreographerConnection)});
+}
+
auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
// the layer could be in either the active or inactive map, try both
auto it = mActiveLayerInfos.find(id);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5022906..68e7030 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -27,6 +27,8 @@
#include <utility>
#include <vector>
+#include "EventThread.h"
+
#include "RefreshRateSelector.h"
namespace android {
@@ -80,6 +82,9 @@
// return the frames per second of the layer with the given sequence id.
float getLayerFramerate(nsecs_t now, int32_t id) const;
+ void attachChoreographer(int32_t layerId,
+ const sp<EventThreadConnection>& choreographerConnection);
+
private:
friend class LayerHistoryTest;
friend class TestableScheduler;
@@ -117,6 +122,10 @@
LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
+ // Map keyed by layer ID (sequence) to choreographer connections.
+ std::unordered_multimap<int32_t, wp<EventThreadConnection>> mAttachedChoreographers
+ GUARDED_BY(mLock);
+
uint32_t mDisplayArea = 0;
// Whether to emit systrace output and debug logs.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e6f4665..eed57ef 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -47,6 +47,7 @@
#include "Display/DisplayMap.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
+#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
#include "SurfaceFlingerProperties.h"
#include "VSyncPredictor.h"
@@ -232,15 +233,21 @@
}
sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, EventRegistrationFlags eventRegistration) {
- return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
+ EventThread* eventThread, EventRegistrationFlags eventRegistration,
+ const sp<IBinder>& layerHandle) {
+ int32_t layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
+ auto connection = eventThread->createEventConnection([&] { resync(); }, eventRegistration);
+ mLayerHistory.attachChoreographer(layerId, connection);
+ return connection;
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- ConnectionHandle handle, EventRegistrationFlags eventRegistration) {
+ ConnectionHandle handle, EventRegistrationFlags eventRegistration,
+ const sp<IBinder>& layerHandle) {
std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration);
+ return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration,
+ layerHandle);
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 8dc2def..8c8fc21 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -147,7 +147,8 @@
std::chrono::nanoseconds readyDuration);
sp<IDisplayEventConnection> createDisplayEventConnection(
- ConnectionHandle, EventRegistrationFlags eventRegistration = {});
+ ConnectionHandle, EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
@@ -302,7 +303,8 @@
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(
- EventThread*, EventRegistrationFlags eventRegistration = {});
+ EventThread*, EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
index bd4e3c2..ba1459a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Time.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -26,7 +26,7 @@
namespace scheduler {
// TODO(b/185535769): Pull Clock.h to libscheduler to reuse this.
-using SchedulerClock = std::chrono::high_resolution_clock;
+using SchedulerClock = std::chrono::steady_clock;
static_assert(SchedulerClock::is_steady);
} // namespace scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6020aba..a0c3eb0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -173,6 +173,8 @@
using namespace hardware::configstore;
using namespace hardware::configstore::V1_0;
using namespace sysprop;
+using ftl::Flags;
+using namespace ftl::flag_operators;
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
using aidl::android::hardware::graphics::composer3::Capability;
@@ -470,6 +472,10 @@
mPowerHintSessionMode =
{.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
.early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
+ mLayerLifecycleManagerEnabled =
+ base::GetBoolProperty("debug.sf.enable_layer_lifecycle_manager"s, false);
+ mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
+ base::GetBoolProperty("debug.sf.enable_legacy_frontend"s, true);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1988,13 +1994,14 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
- gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) {
+ gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
+ const sp<IBinder>& layerHandle) {
const auto& handle =
vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
? mSfConnectionHandle
: mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, eventRegistration);
+ return mScheduler->createDisplayEventConnection(handle, eventRegistration, layerHandle);
}
void SurfaceFlinger::scheduleCommit(FrameHint hint) {
@@ -2136,6 +2143,110 @@
}
}
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+ bool transactionsFlushed,
+ bool& outTransactionsAreEmpty) {
+ bool needsTraversal = false;
+ if (transactionsFlushed) {
+ needsTraversal |= commitMirrorDisplays(vsyncId);
+ needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
+ needsTraversal |= applyTransactions(update.transactions, vsyncId);
+ }
+ outTransactionsAreEmpty = !needsTraversal;
+ const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
+ if (shouldCommit) {
+ commitTransactions();
+ }
+
+ bool mustComposite = latchBuffers() || shouldCommit;
+ updateLayerGeometry();
+ return mustComposite;
+}
+
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update,
+ bool transactionsFlushed, bool& outTransactionsAreEmpty) {
+ using Changes = frontend::RequestedLayerState::Changes;
+ ATRACE_NAME("updateLayerSnapshots");
+ {
+ mLayerLifecycleManager.addLayers(std::move(update.newLayers));
+ mLayerLifecycleManager.applyTransactions(update.transactions);
+ mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
+ for (auto& legacyLayer : update.layerCreatedStates) {
+ sp<Layer> layer = legacyLayer.layer.promote();
+ if (layer) {
+ mLegacyLayers[layer->sequence] = layer;
+ }
+ }
+ }
+ if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
+ ATRACE_NAME("LayerHierarchyBuilder:update");
+ mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
+ mLayerLifecycleManager.getDestroyedLayers());
+ }
+
+ applyAndCommitDisplayTransactionStates(update.transactions);
+
+ {
+ ATRACE_NAME("LayerSnapshotBuilder:update");
+ frontend::LayerSnapshotBuilder::Args args{.root = mLayerHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLayerLifecycleManager,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = mFrontEndDisplayInfosChanged,
+ .globalShadowSettings =
+ mDrawingState.globalShadowSettings,
+ .supportsBlur = mSupportsBlur,
+ .forceFullDamage = mForceFullDamage};
+ mLayerSnapshotBuilder.update(args);
+ }
+
+ if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
+ Changes::Hierarchy)) {
+ mUpdateInputInfo = true;
+ }
+ if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
+ Changes::Visibility)) {
+ mVisibleRegionsDirty = true;
+ }
+ outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
+ const bool mustComposite = mLayerLifecycleManager.getGlobalChanges().get() != 0;
+ {
+ ATRACE_NAME("LLM:commitChanges");
+ mLayerLifecycleManager.commitChanges();
+ }
+
+ if (!mLegacyFrontEndEnabled) {
+ ATRACE_NAME("DisplayCallbackAndStatsUpdates");
+ applyTransactions(update.transactions, vsyncId);
+
+ bool newDataLatched = false;
+ for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ if (!snapshot->changes.test(Changes::Buffer)) continue;
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ mLayersWithQueuedFrames.emplace(it->second);
+ newDataLatched = true;
+ if (!snapshot->isVisible) break;
+
+ Region visibleReg;
+ visibleReg.set(snapshot->transformedBoundsWithoutTransparentRegion);
+ invalidateLayerStack(snapshot->outputFilter, visibleReg);
+ }
+
+ for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
+ mLegacyLayers.erase(destroyedLayer->id);
+ }
+
+ // enter boot animation on first buffer latch
+ if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
+ ALOGI("Enter boot animation");
+ mBootStage = BootStage::BOOTANIMATION;
+ }
+ commitTransactions();
+ }
+ return mustComposite;
+}
+
bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
// The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
@@ -2275,45 +2386,34 @@
mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(),
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
- bool needsTraversal = false;
- if (clearTransactionFlags(eTransactionFlushNeeded)) {
- // Locking:
- // 1. to prevent onHandleDestroyed from being called while the state lock is held,
- // we must keep a copy of the transactions (specifically the composer
- // states) around outside the scope of the lock.
- // 2. Transactions and created layers do not share a lock. To prevent applying
- // transactions with layers still in the createdLayer queue, flush the transactions
- // before committing the created layers.
- std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
- needsTraversal |= commitMirrorDisplays(vsyncId);
- needsTraversal |= commitCreatedLayers(vsyncId);
- needsTraversal |= applyTransactions(transactions, vsyncId);
+ const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
+ LifecycleUpdate updates;
+ if (flushTransactions) {
+ updates = flushLifecycleUpdates();
}
-
- const bool shouldCommit =
- (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
- if (shouldCommit) {
- commitTransactions();
+ bool transactionsAreEmpty;
+ if (mLegacyFrontEndEnabled) {
+ mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
+ transactionsAreEmpty);
+ }
+ if (mLayerLifecycleManagerEnabled) {
+ mustComposite |=
+ updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty);
}
if (transactionFlushNeeded()) {
setTransactionFlags(eTransactionFlushNeeded);
}
- mustComposite |= shouldCommit;
- mustComposite |= latchBuffers();
-
// This has to be called after latchBuffers because we want to include the layers that have
// been latched in the commit callback
- if (!needsTraversal) {
+ if (transactionsAreEmpty) {
// Invoke empty transaction callbacks early.
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
} else {
// Invoke OnCommit callbacks.
mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
}
-
- updateLayerGeometry();
}
// Layers need to get updated (in the previous line) before we can use them for
@@ -2400,15 +2500,6 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- std::vector<Layer*> layers;
-
- mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) {
- if (auto layerFE = layer->getCompositionEngineLayerFE()) {
- layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
- refreshArgs.layers.push_back(layerFE);
- layers.push_back(layer);
- }
- });
refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
@@ -2435,17 +2526,13 @@
// the scheduler.
const auto presentTime = systemTime();
- {
- std::vector<LayerSnapshotGuard> layerSnapshotGuards;
- for (Layer* layer : layers) {
- layerSnapshotGuards.emplace_back(layer);
- }
- mCompositionEngine->present(refreshArgs);
- }
+ std::vector<std::pair<Layer*, LayerFE*>> layers =
+ moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/false, vsyncId.value);
+ mCompositionEngine->present(refreshArgs);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
- for (auto& layer : layers) {
- CompositionResult compositionResult{
- layer->getCompositionEngineLayerFE()->stealCompositionResult()};
+ for (auto [layer, layerFE] : layers) {
+ CompositionResult compositionResult{layerFE->stealCompositionResult()};
layer->onPreComposition(compositionResult.refreshStartTime);
for (auto releaseFence : compositionResult.releaseFences) {
layer->onLayerDisplayed(releaseFence);
@@ -2539,7 +2626,7 @@
for (auto& layer : mLayersPendingRefresh) {
Region visibleReg;
visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(layer, visibleReg);
+ invalidateLayerStack(layer->getOutputFilter(), visibleReg);
}
mLayersPendingRefresh.clear();
}
@@ -2614,12 +2701,13 @@
ATRACE_CALL();
ALOGV(__func__);
- const auto* display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ const auto* defaultDisplay = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
+ if (defaultDisplay &&
+ defaultDisplay->getCompositionDisplay()->getState().usesClientComposition) {
glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(display->getCompositionDisplay()
+ std::make_shared<FenceTime>(defaultDisplay->getCompositionDisplay()
->getRenderSurface()
->getClientTargetAcquireFence());
} else {
@@ -2628,8 +2716,9 @@
mPreviousPresentFences[1] = mPreviousPresentFences[0];
- auto presentFence =
- display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
+ auto presentFence = defaultDisplay
+ ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
+ : Fence::NO_FENCE;
auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
mPreviousPresentFences[0] = {presentFence, presentFenceTime};
@@ -2660,7 +2749,7 @@
presentLatency.ns());
for (const auto& layer: mLayersWithQueuedFrames) {
- layer->onPostComposition(display, glCompositionDoneFenceTime, presentFenceTime,
+ layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
compositorTiming);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -2733,7 +2822,7 @@
for (const auto& [id, physicalDisplay] : mPhysicalDisplays) {
if (auto displayDevice = getDisplayDeviceLocked(id);
displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) {
- auto presentFenceTimeI = display && display->getPhysicalId() == id
+ auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id
? std::move(presentFenceTime)
: std::make_shared<FenceTime>(getHwComposer().getPresentFence(id));
if (presentFenceTimeI->isValid()) {
@@ -2744,11 +2833,11 @@
}
const bool isDisplayConnected =
- display && getHwComposer().isConnected(display->getPhysicalId());
+ defaultDisplay && getHwComposer().isConnected(defaultDisplay->getPhysicalId());
if (!hasSyncFramework) {
- if (isDisplayConnected && display->isPoweredOn()) {
- mScheduler->enableHardwareVsync(display->getPhysicalId());
+ if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
+ mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
}
}
@@ -2756,7 +2845,7 @@
const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
- if (isDisplayConnected && !display->isPoweredOn()) {
+ if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
getRenderEngine().cleanupPostRender();
return;
}
@@ -2795,9 +2884,10 @@
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const auto display =
+ const std::optional<const DisplayDevice*> displayOpt =
layerStackToDisplay.get(layer->getLayerSnapshot()->outputFilter.layerStack);
- layer->updateTrustedPresentationState(display->get(), layer->getLayerSnapshot(),
+ const DisplayDevice* display = displayOpt.value_or(nullptr);
+ layer->updateTrustedPresentationState(display, layer->getLayerSnapshot(),
nanoseconds_to_milliseconds(callTime), false);
});
}
@@ -3397,7 +3487,8 @@
void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) {
// Commit display transactions.
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
- if (displayTransactionNeeded) {
+ mFrontEndDisplayInfosChanged = displayTransactionNeeded;
+ if (displayTransactionNeeded && !mLayerLifecycleManagerEnabled) {
processDisplayChangesLocked();
mFrontEndDisplayInfos.clear();
for (const auto& [_, display] : mDisplays) {
@@ -3488,7 +3579,7 @@
// this layer is not visible anymore
Region visibleReg;
visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(sp<Layer>::fromExisting(layer), visibleReg);
+ invalidateLayerStack(layer->getOutputFilter(), visibleReg);
}
});
}
@@ -3576,16 +3667,23 @@
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
- mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
- if (!layer->needsInputInfo()) return;
+ if (mLayerLifecycleManagerEnabled) {
+ mLayerSnapshotBuilder.forEachInputSnapshot(
+ [&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
+ outWindowInfos.push_back(snapshot.inputInfo);
+ });
+ } else {
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ if (!layer->needsInputInfo()) return;
+ const auto opt =
+ mFrontEndDisplayInfos.get(layer->getLayerStack())
+ .transform([](const frontend::DisplayInfo& info) {
+ return Layer::InputDisplayArgs{&info.transform, info.isSecure};
+ });
- const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack())
- .transform([](const frontend::DisplayInfo& info) {
- return Layer::InputDisplayArgs{&info.transform, info.isSecure};
- });
-
- outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
- });
+ outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
+ });
+ }
sNumWindowInfos = outWindowInfos.size();
@@ -3602,17 +3700,9 @@
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
-
- std::vector<LayerSnapshotGuard> layerSnapshotGuards;
- mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) {
- if (layer->getLayerSnapshot()->compositionType ==
- aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
- layer->updateSnapshot(false /* updateGeometry */);
- layerSnapshotGuards.emplace_back(layer);
- }
- });
-
+ auto layers = moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/true, 0);
mCompositionEngine->updateCursorAsync(refreshArgs);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
}
void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
@@ -3787,10 +3877,10 @@
}
}
-void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
+void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) {
for (const auto& [token, displayDevice] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto display = displayDevice->getCompositionDisplay();
- if (display->includesLayer(layer->getOutputFilter())) {
+ if (display->includesLayer(layerFilter)) {
display->editState().dirtyRegion.orSelf(dirty);
}
}
@@ -3910,6 +4000,7 @@
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
+ mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
}
setTransactionFlags(eTransactionNeeded);
@@ -3921,14 +4012,18 @@
}
uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
- return mTransactionFlags.fetch_and(~mask) & mask;
+ uint32_t transactionFlags = mTransactionFlags.fetch_and(~mask);
+ ATRACE_INT("mTransactionFlags", transactionFlags);
+ return transactionFlags & mask;
}
void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
const sp<IBinder>& applyToken, FrameHint frameHint) {
mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
+ uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
+ ATRACE_INT("mTransactionFlags", transactionFlags);
- if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
+ if (const bool scheduled = transactionFlags & mask; !scheduled) {
scheduleCommit(frameHint);
} else if (frameHint == FrameHint::kActive) {
// Even if the next frame is already scheduled, we should reset the idle timer
@@ -4243,9 +4338,8 @@
}(state.flags);
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
- setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
mTransactionHandler.queueTransaction(std::move(state));
-
+ setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
return NO_ERROR;
}
@@ -4260,9 +4354,11 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- for (DisplayState& display : displays) {
- display.sanitize(permissions);
- transactionFlags |= setDisplayStateLocked(display);
+ if (!mLayerLifecycleManagerEnabled) {
+ for (DisplayState& display : displays) {
+ display.sanitize(permissions);
+ transactionFlags |= setDisplayStateLocked(display);
+ }
}
// start and end registration for listeners w/ no surface so they can get their callback. Note
@@ -4274,9 +4370,16 @@
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
- clientStateFlags |=
- setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
- isAutoTimestamp, postTime, permissions, transactionId);
+ if (mLegacyFrontEndEnabled) {
+ clientStateFlags |=
+ setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
+ isAutoTimestamp, postTime, permissions, transactionId);
+
+ } else /*mLayerLifecycleManagerEnabled*/ {
+ clientStateFlags |= updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState,
+ desiredPresentTime, isAutoTimestamp,
+ postTime, permissions, transactionId);
+ }
if ((flags & eAnimation) && resolvedState.state.surface) {
if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
@@ -4309,8 +4412,8 @@
bool needsTraversal = false;
if (transactionFlags) {
- // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
- // so we don't have to wake up again next frame to preform an unnecessary traversal.
+ // We are on the main thread, we are about to perform a traversal. Clear the traversal bit
+ // so we don't have to wake up again next frame to perform an unnecessary traversal.
if (transactionFlags & eTraversalNeeded) {
transactionFlags = transactionFlags & (~eTraversalNeeded);
needsTraversal = true;
@@ -4323,6 +4426,42 @@
return needsTraversal;
}
+bool SurfaceFlinger::applyAndCommitDisplayTransactionStates(
+ std::vector<TransactionState>& transactions) {
+ Mutex::Autolock _l(mStateLock);
+ bool needsTraversal = false;
+ uint32_t transactionFlags = 0;
+ for (auto& transaction : transactions) {
+ for (DisplayState& display : transaction.displays) {
+ display.sanitize(transaction.permissions);
+ transactionFlags |= setDisplayStateLocked(display);
+ }
+ }
+
+ if (transactionFlags) {
+ // We are on the main thread, we are about to perform a traversal. Clear the traversal bit
+ // so we don't have to wake up again next frame to perform an unnecessary traversal.
+ if (transactionFlags & eTraversalNeeded) {
+ transactionFlags = transactionFlags & (~eTraversalNeeded);
+ needsTraversal = true;
+ }
+ if (transactionFlags) {
+ setTransactionFlags(transactionFlags);
+ }
+ }
+
+ mFrontEndDisplayInfosChanged = mTransactionFlags & eDisplayTransactionNeeded;
+ if (mFrontEndDisplayInfosChanged && !mLegacyFrontEndEnabled) {
+ processDisplayChangesLocked();
+ mFrontEndDisplayInfos.clear();
+ for (const auto& [_, display] : mDisplays) {
+ mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+ }
+ }
+
+ return needsTraversal;
+}
+
uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
const ssize_t index = mCurrentState.displays.indexOfKey(s.token);
if (index < 0) return 0;
@@ -4704,7 +4843,11 @@
s.trustedPresentationListener);
}
- if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+ if (layer->setTransactionCompletedListeners(callbackHandles,
+ layer->willPresentCurrentTransaction())) {
+ flags |= eTraversalNeeded;
+ }
+
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -4717,6 +4860,94 @@
return flags;
}
+uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& frameTimelineInfo,
+ ResolvedComposerState& composerState,
+ int64_t desiredPresentTime,
+ bool isAutoTimestamp, int64_t postTime,
+ uint32_t permissions,
+ uint64_t transactionId) {
+ layer_state_t& s = composerState.state;
+ s.sanitize(permissions);
+ const nsecs_t latchTime = systemTime();
+ bool unused;
+
+ std::vector<ListenerCallbacks> filteredListeners;
+ for (auto& listener : s.listeners) {
+ // Starts a registration but separates the callback ids according to callback type. This
+ // allows the callback invoker to send on latch callbacks earlier.
+ // note that startRegistration will not re-register if the listener has
+ // already be registered for a prior surface control
+
+ ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+ if (!onCommitCallbacks.callbackIds.empty()) {
+ filteredListeners.push_back(onCommitCallbacks);
+ }
+
+ ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+ if (!onCompleteCallbacks.callbackIds.empty()) {
+ filteredListeners.push_back(onCompleteCallbacks);
+ }
+ }
+
+ const uint64_t what = s.what;
+ uint32_t flags = 0;
+ sp<Layer> layer = nullptr;
+ if (s.surface) {
+ layer = LayerHandle::getLayer(s.surface);
+ } else {
+ // The client may provide us a null handle. Treat it as if the layer was removed.
+ ALOGW("Attempt to set client state with a null layer handle");
+ }
+ if (layer == nullptr) {
+ for (auto& [listener, callbackIds] : s.listeners) {
+ mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
+ sp<CallbackHandle>::make(listener, callbackIds, s.surface));
+ }
+ return 0;
+ }
+ if (what & layer_state_t::eProducerDisconnect) {
+ layer->onDisconnect();
+ }
+ std::optional<nsecs_t> dequeueBufferTimestamp;
+ if (what & layer_state_t::eMetadataChanged) {
+ dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
+ }
+
+ std::vector<sp<CallbackHandle>> callbackHandles;
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+ for (auto& [listener, callbackIds] : filteredListeners) {
+ callbackHandles.emplace_back(
+ sp<CallbackHandle>::make(listener, callbackIds, s.surface));
+ }
+ }
+ if (what & layer_state_t::eSidebandStreamChanged) {
+ if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eBufferChanged) {
+ if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
+ desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
+ frameTimelineInfo)) {
+ layer->latchBuffer(unused, latchTime);
+ flags |= eTraversalNeeded;
+ }
+ mLayersWithQueuedFrames.emplace(layer);
+ } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
+ }
+
+ if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+ layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+ s.trustedPresentationListener);
+ }
+
+ const auto& snapshot = mLayerSnapshotBuilder.getSnapshot(layer->getSequence());
+ bool willPresentCurrentTransaction =
+ snapshot && (snapshot->hasReadyFrame || snapshot->sidebandStreamHasFrame);
+ if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
+ flags |= eTraversalNeeded;
+ return flags;
+}
+
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
bool hasChanges = mInputWindowCommands.merge(inputWindowCommands);
return hasChanges ? eTraversalNeeded : 0;
@@ -4785,6 +5016,7 @@
LayerCreationArgs mirrorArgs(args);
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
mirrorArgs.addToRoot = true;
+ mirrorArgs.layerStackToMirror = layerStack;
result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
outResult.layerId = rootMirrorLayer->sequence;
outResult.layerName = String16(rootMirrorLayer->getDebugName());
@@ -4887,7 +5119,12 @@
setTransactionFlags(eTransactionNeeded);
}
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t /* layerId */) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
+ {
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ mDestroyedHandles.emplace_back(layerId);
+ }
+
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
@@ -4895,6 +5132,8 @@
if (mTransactionTracing) {
mTransactionTracing->onHandleRemoved(handle);
}
+
+ setTransactionFlags(eTransactionFlushNeeded);
}
void SurfaceFlinger::onInitializeDisplays() {
@@ -6458,10 +6697,15 @@
args.useIdentityTransform, args.captureSecureLayers);
});
- auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, args.uid, visitor);
- };
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ GetLayerSnapshotsFunction getLayerSnapshots;
+ if (mLayerLifecycleManagerEnabled) {
+ getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, args.uid);
+ } else {
+ auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, args.uid, visitor);
+ };
+ getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ }
auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
args.pixelFormat, args.allowProtected, args.grayscale,
@@ -6495,10 +6739,15 @@
false /* captureSecureLayers */);
});
- auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
- };
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ GetLayerSnapshotsFunction getLayerSnapshots;
+ if (mLayerLifecycleManagerEnabled) {
+ getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID);
+ } else {
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
+ };
+ getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ }
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
@@ -6593,29 +6842,37 @@
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
childrenOnly, args.captureSecureLayers);
});
-
- auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
- parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->isVisible()) {
- return;
- } else if (args.childrenOnly && layer == parent.get()) {
- return;
- } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
- return;
- }
-
- auto p = sp<Layer>::fromExisting(layer);
- while (p != nullptr) {
- if (excludeLayerIds.count(p->sequence) != 0) {
+ GetLayerSnapshotsFunction getLayerSnapshots;
+ if (mLayerLifecycleManagerEnabled) {
+ FloatRect parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
+ : crop.toFloatRect();
+ getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid,
+ std::move(excludeLayerIds),
+ args.childrenOnly, parentCrop);
+ } else {
+ auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
+ parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ if (!layer->isVisible()) {
+ return;
+ } else if (args.childrenOnly && layer == parent.get()) {
+ return;
+ } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
return;
}
- p = p->getParent();
- }
- visitor(layer);
- });
- };
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ auto p = sp<Layer>::fromExisting(layer);
+ while (p != nullptr) {
+ if (excludeLayerIds.count(p->sequence) != 0) {
+ return;
+ }
+ p = p->getParent();
+ }
+
+ visitor(layer);
+ });
+ };
+ getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ }
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
@@ -7397,24 +7654,18 @@
return true;
}
-bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId) {
- std::vector<LayerCreatedState> createdLayers;
- {
- std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- createdLayers = std::move(mCreatedLayers);
- mCreatedLayers.clear();
- if (createdLayers.size() == 0) {
- return false;
- }
+bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId,
+ std::vector<LayerCreatedState>& createdLayers) {
+ if (createdLayers.size() == 0) {
+ return false;
}
Mutex::Autolock _l(mStateLock);
for (const auto& createdLayer : createdLayers) {
handleLayerCreatedLocked(createdLayer, vsyncId);
}
- createdLayers.clear();
mLayersAdded = true;
- return true;
+ return mLayersAdded;
}
void SurfaceFlinger::updateLayerMetadataSnapshot() {
@@ -7442,6 +7693,150 @@
});
}
+void SurfaceFlinger::moveSnapshotsFromCompositionArgs(
+ compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::vector<std::pair<Layer*, LayerFE*>>& layers) {
+ if (mLayerLifecycleManagerEnabled) {
+ std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots =
+ mLayerSnapshotBuilder.getSnapshots();
+ for (auto [_, layerFE] : layers) {
+ auto i = layerFE->mSnapshot->globalZ;
+ snapshots[i] = std::move(layerFE->mSnapshot);
+ }
+ }
+ if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+ for (auto [layer, layerFE] : layers) {
+ layer->updateLayerSnapshot(std::move(layerFE->mSnapshot));
+ }
+ }
+}
+
+std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToCompositionArgs(
+ compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) {
+ std::vector<std::pair<Layer*, LayerFE*>> layers;
+ if (mLayerLifecycleManagerEnabled) {
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (cursorOnly &&
+ snapshot->compositionType !=
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
+ return;
+ }
+
+ if (!snapshot->hasSomethingToDraw()) {
+ return;
+ }
+
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+ layerFE->mSnapshot = std::move(snapshot);
+ refreshArgs.layers.push_back(layerFE);
+ layers.emplace_back(legacyLayer.get(), layerFE.get());
+ });
+ }
+ if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+ mDrawingState.traverseInZOrder([&refreshArgs, cursorOnly, &layers](Layer* layer) {
+ if (auto layerFE = layer->getCompositionEngineLayerFE()) {
+ if (cursorOnly &&
+ layer->getLayerSnapshot()->compositionType !=
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR)
+ return;
+ layer->updateSnapshot(/* refreshArgs.updatingGeometryThisFrame */ true);
+ layerFE->mSnapshot = layer->stealLayerSnapshot();
+ refreshArgs.layers.push_back(layerFE);
+ layers.emplace_back(layer, layerFE.get());
+ }
+ });
+ }
+
+ return layers;
+}
+
+std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
+SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
+ uint32_t uid) {
+ return [this, layerStack, uid]() {
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+ for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
+ continue;
+ }
+ if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
+ continue;
+ }
+ if (!snapshot->isVisible || !snapshot->hasSomethingToDraw()) {
+ continue;
+ }
+
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFE = getFactory().createLayerFE(legacyLayer->getName());
+ layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
+ layers.emplace_back(legacyLayer.get(), std::move(layerFE));
+ }
+
+ return layers;
+ };
+}
+
+std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
+SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid,
+ std::unordered_set<uint32_t> excludeLayerIds,
+ bool childrenOnly, const FloatRect& parentCrop) {
+ return [this, excludeLayerIds = std::move(excludeLayerIds), uid, rootLayerId, childrenOnly,
+ parentCrop]() {
+ frontend::LayerSnapshotBuilder::Args
+ args{.root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly),
+ .layerLifecycleManager = mLayerLifecycleManager,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = true,
+ .globalShadowSettings = mDrawingState.globalShadowSettings,
+ .supportsBlur = mSupportsBlur,
+ .forceFullDamage = mForceFullDamage,
+ .parentCrop = {parentCrop},
+ .excludeLayerIds = std::move(excludeLayerIds)};
+ mLayerSnapshotBuilder.update(args);
+
+ auto getLayerSnapshotsFn = getLayerSnapshotsForScreenshots({}, uid);
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
+ args.root = mLayerHierarchyBuilder.getHierarchy();
+ args.parentCrop.reset();
+ args.excludeLayerIds.clear();
+ mLayerSnapshotBuilder.update(args);
+ return layers;
+ };
+}
+
+SurfaceFlinger::LifecycleUpdate SurfaceFlinger::flushLifecycleUpdates() {
+ LifecycleUpdate update;
+ ATRACE_NAME("TransactionHandler:flushTransactions");
+ // Locking:
+ // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+ // we must keep a copy of the transactions (specifically the composer
+ // states) around outside the scope of the lock.
+ // 2. Transactions and created layers do not share a lock. To prevent applying
+ // transactions with layers still in the createdLayer queue, flush the transactions
+ // before committing the created layers.
+ update.transactions = mTransactionHandler.flushTransactions();
+ {
+ // TODO(b/238781169) lockless queue this and keep order.
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ update.layerCreatedStates = std::move(mCreatedLayers);
+ mCreatedLayers.clear();
+ update.newLayers = std::move(mNewLayers);
+ mNewLayers.clear();
+ update.destroyedHandles = std::move(mDestroyedHandles);
+ mDestroyedHandles.clear();
+ }
+ return update;
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -7455,9 +7850,9 @@
binder::Status SurfaceComposerAIDL::createDisplayEventConnection(
VsyncSource vsyncSource, EventRegistration eventRegistration,
- sp<IDisplayEventConnection>* outConnection) {
+ const sp<IBinder>& layerHandle, sp<IDisplayEventConnection>* outConnection) {
sp<IDisplayEventConnection> conn =
- mFlinger->createDisplayEventConnection(vsyncSource, eventRegistration);
+ mFlinger->createDisplayEventConnection(vsyncSource, eventRegistration, layerHandle);
if (conn == nullptr) {
*outConnection = nullptr;
return binderStatusFromStatusT(BAD_VALUE);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1eb1fda..0bd15dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -71,7 +71,9 @@
#include "FlagManager.h"
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshot.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
#include "Scheduler/ISchedulerCallback.h"
@@ -450,6 +452,26 @@
FINISHED,
};
+ struct LayerCreatedState {
+ LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
+ : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
+ wp<Layer> layer;
+ // Indicates the initial parent of the created layer, only used for creating layer in
+ // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+ wp<Layer> initialParent;
+ // Indicates whether the layer getting created should be added at root if there's no parent
+ // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
+ // be added offscreen.
+ bool addToRoot;
+ };
+
+ struct LifecycleUpdate {
+ std::vector<TransactionState> transactions;
+ std::vector<LayerCreatedState> layerCreatedStates;
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
+ std::vector<uint32_t> destroyedHandles;
+ };
+
template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
static Dumper dumper(F&& dump) {
using namespace std::placeholders;
@@ -509,7 +531,8 @@
sp<IDisplayEventConnection> createDisplayEventConnection(
gui::ISurfaceComposer::VsyncSource vsyncSource =
gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- EventRegistrationFlags eventRegistration = {});
+ EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
@@ -688,6 +711,17 @@
void updateLayerGeometry();
void updateLayerMetadataSnapshot();
+ std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
+ compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly,
+ int64_t vsyncId);
+ void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::vector<std::pair<Layer*, LayerFE*>>& layers);
+ bool updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+ bool transactionsFlushed, bool& out)
+ REQUIRES(kMainThreadContext);
+ bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed,
+ bool& out) REQUIRES(kMainThreadContext);
+ LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger();
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -715,6 +749,8 @@
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
+ bool applyAndCommitDisplayTransactionStates(std::vector<TransactionState>& transactions)
+ REQUIRES(kMainThreadContext);
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
@@ -730,7 +766,10 @@
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint32_t permissions, uint64_t transactionId)
REQUIRES(mStateLock);
-
+ uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ int64_t postTime, uint32_t permissions,
+ uint64_t transactionId) REQUIRES(mStateLock);
uint32_t getTransactionFlags() const;
// Sets the masked bits, and schedules a commit if needed.
@@ -888,7 +927,7 @@
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
- void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
+ void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty);
ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
REQUIRES(mStateLock) {
@@ -1154,6 +1193,7 @@
// Set if LayerMetadata has changed since the last LayerMetadata snapshot.
bool mLayerMetadataSnapshotNeeded = false;
+ // TODO(b/238781169) validate these on composition
// Tracks layers that have pending frames which are candidates for being
// latched.
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
@@ -1325,23 +1365,11 @@
GUARDED_BY(mStateLock);
mutable std::mutex mCreatedLayersLock;
- struct LayerCreatedState {
- LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
- : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
- wp<Layer> layer;
- // Indicates the initial parent of the created layer, only used for creating layer in
- // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
- wp<Layer> initialParent;
- // Indicates whether the layer getting created should be added at root if there's no parent
- // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
- // be added offscreen.
- bool addToRoot;
- };
// A temporay pool that store the created layers and will be added to current state in main
// thread.
std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
- bool commitCreatedLayers(VsyncId);
+ bool commitCreatedLayers(VsyncId, std::vector<LayerCreatedState>& createdLayers);
void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock);
mutable std::mutex mMirrorDisplayLock;
@@ -1363,6 +1391,11 @@
return hasDisplay(
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
+ std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
+ std::optional<ui::LayerStack> layerStack, uint32_t uid);
+ std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
+ uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
+ bool childrenOnly, const FloatRect& parentCrop);
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
@@ -1375,6 +1408,18 @@
bool mPowerHintSessionEnabled;
+ bool mLayerLifecycleManagerEnabled = false;
+ bool mLegacyFrontEndEnabled = true;
+
+ frontend::LayerLifecycleManager mLayerLifecycleManager;
+ frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+ frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
+
+ std::vector<uint32_t> mDestroyedHandles;
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
+ // These classes do not store any client state but help with managing transaction callbacks
+ // and stats.
+ std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
struct {
bool late = false;
bool early = false;
@@ -1382,6 +1427,7 @@
TransactionHandler mTransactionHandler;
display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
+ bool mFrontEndDisplayInfosChanged = false;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1391,6 +1437,7 @@
binder::Status bootFinished() override;
binder::Status createDisplayEventConnection(
VsyncSource vsyncSource, EventRegistration eventRegistration,
+ const sp<IBinder>& layerHandle,
sp<gui::IDisplayEventConnection>* outConnection) override;
binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
binder::Status createDisplay(const std::string& displayName, bool secure,
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 11719c4..c088e7b 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -148,7 +148,7 @@
layer->fenceHasSignaled();
layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
const std::vector<sp<CallbackHandle>> callbacks;
- layer->setTransactionCompletedListeners(callbacks);
+ layer->setTransactionCompletedListeners(callbacks, mFdp.ConsumeBool());
std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
renderengine::mock::FakeExternalTexture>(mFdp.ConsumeIntegral<uint32_t>(),