Merge "[Lut HAL backend] implementation 3rd patch." into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b012243..4758607 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -4656,7 +4656,7 @@
 void Dumpstate::TakeScreenshot(const std::string& path) {
     const std::string& real_path = path.empty() ? screenshot_path_ : path;
     int status =
-        RunCommand("", {"/system/bin/screencap", "-p", real_path},
+        RunCommand("", {"screencap", "-p", real_path},
                    CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
     if (status == 0) {
         MYLOGD("Screenshot saved on %s\n", real_path.c_str());
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 79cdbca..495e0bd 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -843,6 +843,44 @@
     AKEYCODE_EMOJI_PICKER = 317,
     /** Take Screenshot */
     AKEYCODE_SCREENSHOT = 318,
+    /** To start dictate to an input field */
+    AKEYCODE_DICTATE = 319,
+    /** AC New */
+    AKEYCODE_NEW = 320,
+    /** AC Close */
+    AKEYCODE_CLOSE = 321,
+    /** To toggle 'Do Not Disturb' mode */
+    AKEYCODE_DO_NOT_DISTURB = 322,
+    /** To Print */
+    AKEYCODE_PRINT = 323,
+    /** To Lock the screen */
+    AKEYCODE_LOCK = 324,
+    /** To toggle fullscreen mode (on the current application) */
+    AKEYCODE_FULLSCREEN = 325,
+    /** F13 key */
+    AKEYCODE_F13 = 326,
+    /** F14 key */
+    AKEYCODE_F14 = 327,
+    /** F15 key */
+    AKEYCODE_F15 = 328,
+    /** F16 key */
+    AKEYCODE_F16 = 329,
+    /** F17 key */
+    AKEYCODE_F17 = 330,
+    /** F18 key */
+    AKEYCODE_F18 = 331,
+    /** F19 key */
+    AKEYCODE_F19 = 332,
+    /** F20 key */
+    AKEYCODE_F20 = 333,
+    /** F21 key */
+    AKEYCODE_F21 = 334,
+    /** F22 key */
+    AKEYCODE_F22 = 335,
+    /** F23 key */
+    AKEYCODE_F23 = 336,
+    /** F24 key */
+    AKEYCODE_F24 = 337,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 807f850..a0a8b35 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2133,13 +2133,13 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
-        const sp<SurfaceControl>& sc, const WindowInfo& info) {
+        const sp<SurfaceControl>& sc, sp<WindowInfoHandle> info) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->windowInfoHandle = new WindowInfoHandle(info);
+    s->windowInfoHandle = std::move(info);
     s->what |= layer_state_t::eInputInfoChanged;
     return *this;
 }
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e9262b3..7c6b341 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -687,7 +687,8 @@
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
 
-        Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
+        Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc,
+                                        sp<gui::WindowInfoHandle> info);
         Transaction& setFocusedWindow(const gui::FocusRequest& request);
 
         Transaction& addWindowInfosReportedListener(
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 17630e3..0e84d68 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -112,7 +112,7 @@
 
         mInputFlinger = getInputFlinger();
         if (noInputChannel) {
-            mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
+            mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
         } else {
             android::os::InputChannelCore tempChannel;
             android::binder::Status result =
@@ -121,21 +121,21 @@
                 ADD_FAILURE() << "binder call to createInputChannel failed";
             }
             mClientChannel = InputChannel::create(std::move(tempChannel));
-            mInputInfo.token = mClientChannel->getConnectionToken();
+            mInputInfo->editInfo()->token = mClientChannel->getConnectionToken();
             mInputConsumer = new InputConsumer(mClientChannel);
         }
 
-        mInputInfo.name = "Test info";
-        mInputInfo.dispatchingTimeout = 5s;
-        mInputInfo.globalScaleFactor = 1.0;
-        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+        mInputInfo->editInfo()->name = "Test info";
+        mInputInfo->editInfo()->dispatchingTimeout = 5s;
+        mInputInfo->editInfo()->globalScaleFactor = 1.0;
+        mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height));
 
         InputApplicationInfo aInfo;
         aInfo.token = new BBinder();
         aInfo.name = "Test app info";
         aInfo.dispatchingTimeoutMillis =
                 std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
-        mInputInfo.applicationInfo = aInfo;
+        mInputInfo->editInfo()->applicationInfo = aInfo;
     }
 
     static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
@@ -183,20 +183,6 @@
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
-    InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
-        mClientChannel->waitForMessage(timeout);
-
-        InputEvent* ev;
-        uint32_t seqId;
-        status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
-        if (consumed != OK) {
-            return nullptr;
-        }
-        status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
-        EXPECT_EQ(OK, status) << "Could not send finished signal";
-        return ev;
-    }
-
     void assertFocusChange(bool hasFocus) {
         InputEvent* ev = consumeEvent();
         ASSERT_NE(ev, nullptr);
@@ -314,8 +300,8 @@
     void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) {
         SurfaceComposerClient::Transaction t;
         FocusRequest request;
-        request.token = mInputInfo.token;
-        request.windowName = mInputInfo.name;
+        request.token = mInputInfo->getInfo()->token;
+        request.windowName = mInputInfo->getInfo()->name;
         request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         request.displayId = displayId.val();
         t.setFocusedWindow(request);
@@ -323,14 +309,30 @@
     }
 
 public:
+    // But should be private
+    sp<gui::WindowInfoHandle> mInputInfo = sp<gui::WindowInfoHandle>::make();
     sp<SurfaceControl> mSurfaceControl;
+
+private:
     std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
-    WindowInfo mInputInfo;
-
     PreallocatedInputEventFactory mInputEventFactory;
     InputConsumer* mInputConsumer;
+
+    InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
+        mClientChannel->waitForMessage(timeout);
+
+        InputEvent* ev;
+        uint32_t seqId;
+        status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+        if (consumed != OK) {
+            return nullptr;
+        }
+        status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
+        EXPECT_EQ(OK, status) << "Could not send finished signal";
+        return ev;
+    }
 };
 
 class BlastInputSurface : public InputSurface {
@@ -458,7 +460,7 @@
 
     injectTap(101, 101);
 
-    EXPECT_NE(surface->consumeEvent(), nullptr);
+    surface->expectTap(1, 1);
 }
 
 /**
@@ -521,7 +523,7 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
 
-    fgSurface->mInputInfo.surfaceInset = 5;
+    fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
     fgSurface->showAt(100, 100);
 
     injectTap(106, 106);
@@ -536,8 +538,8 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
 
-    fgSurface->mInputInfo.surfaceInset = 5;
-    fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+    fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
+    fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
     fgSurface->showAt(100, 100);
 
     injectTap(106, 106);
@@ -553,7 +555,7 @@
     std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
     parentSurface->showAt(100, 100);
 
-    childSurface->mInputInfo.surfaceInset = 10;
+    childSurface->mInputInfo->editInfo()->surfaceInset = 10;
     childSurface->showAt(100, 100);
 
     childSurface->doTransaction([&](auto& t, auto& sc) {
@@ -574,7 +576,7 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
 
-    fgSurface->mInputInfo.surfaceInset = 5;
+    fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
     fgSurface->showAt(100, 100);
 
     fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -593,7 +595,7 @@
     bgSurface->showAt(100, 100);
 
     // In case we pass the very big inset without any checking.
-    fgSurface->mInputInfo.surfaceInset = INT32_MAX;
+    fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX;
     fgSurface->showAt(100, 100);
 
     fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -606,13 +608,13 @@
 TEST_F(InputSurfacesTest, touchable_region) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
 
-    surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31});
+    surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31});
 
     surface->showAt(11, 22);
 
     // A tap within the surface but outside the touchable region should not be sent to the surface.
     injectTap(20, 30);
-    EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr);
+    surface->assertNoEvent();
 
     injectTap(31, 52);
     surface->expectTap(20, 30);
@@ -627,7 +629,8 @@
     // Since the surface is offset from the origin, the touchable region will be transformed into
     // display space, which would trigger an overflow or an underflow. Ensure that we are protected
     // against such a situation.
-    fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+    fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+            Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
 
     fgSurface->showAt(100, 100);
 
@@ -642,7 +645,8 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(0, 0);
 
-    fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+    fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+            Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
     fgSurface->showAt(0, 0);
 
     fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -812,7 +816,7 @@
 
 TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
-    surface->mInputInfo.surfaceInset = 5;
+    surface->mInputInfo->editInfo()->surfaceInset = 5;
     surface->showAt(100, 100);
 
     surface->doTransaction([](auto& t, auto& sc) {
@@ -841,11 +845,12 @@
     // Add non touchable window to fully cover touchable window. Window behind gets touch, but
     // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    nonTouchableSurface->mInputInfo->editInfo()
+            ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+    nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
     // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
     // the default obscured/untrusted touch filter introduced in S.
-    nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
+    nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW;
     nonTouchableSurface->showAt(100, 100);
 
     injectTap(190, 199);
@@ -861,10 +866,12 @@
     // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
-    parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    nonTouchableSurface->mInputInfo->editInfo()
+            ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+    parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                          true);
+    nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+    parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
     nonTouchableSurface->showAt(0, 0);
     parentSurface->showAt(100, 100);
 
@@ -885,10 +892,12 @@
     // the touchable window. Window behind gets touch with no obscured flags.
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
-    parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    nonTouchableSurface->mInputInfo->editInfo()
+            ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+    parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                          true);
+    nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+    parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
     nonTouchableSurface->showAt(0, 0);
     parentSurface->showAt(50, 50);
 
@@ -906,8 +915,9 @@
 
     std::unique_ptr<InputSurface> bufferSurface =
             InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                          true);
+    bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
 
     surface->showAt(10, 10);
     bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -921,8 +931,9 @@
 
     std::unique_ptr<BlastInputSurface> bufferSurface =
             BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                          true);
+    bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
 
     surface->showAt(10, 10);
     bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -965,13 +976,14 @@
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
-    surface->mInputInfo.ownerUid = gui::Uid{11111};
+    surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
     surface->doTransaction(
             [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
     std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
-    obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                             true);
+    obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
     obscuringSurface->showAt(100, 100);
     injectTap(101, 101);
     surface->assertNoEvent();
@@ -984,13 +996,14 @@
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
-    surface->mInputInfo.ownerUid = gui::Uid{11111};
+    surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
     surface->doTransaction(
             [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
     std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
-    obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
-    obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+    obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+                                                             true);
+    obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
     obscuringSurface->showAt(190, 190);
 
     injectTap(101, 101);
@@ -1054,7 +1067,7 @@
             BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
 
     surface->showAt(100, 100);
-    bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200));
+    bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200));
     bufferSurface->showAt(100, 100, Rect::EMPTY_RECT);
 
     injectTap(101, 101);
@@ -1097,8 +1110,8 @@
             InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
     containerSurface->doTransaction(
             [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
-    containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
-    containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+    containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+    containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
     parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
     containerSurface->showAt(10, 10, Rect(0, 0, 5, 5));
 
@@ -1124,8 +1137,8 @@
             InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
     containerSurface->doTransaction(
             [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
-    containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
-    containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+    containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+    containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
     parentContainer->doTransaction(
             [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); });
     bgContainer->showAt(0, 0, Rect(0, 0, 100, 100));
@@ -1152,8 +1165,8 @@
 
     std::unique_ptr<InputSurface> containerSurface =
             InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
-    containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
-    containerSurface->mInputInfo.touchableRegionCropHandle =
+    containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+    containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle =
             cropLayer->mSurfaceControl->getHandle();
     containerSurface->showAt(10, 10, Rect::INVALID_RECT);
 
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 8db0ca5..b537feb 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -350,7 +350,26 @@
     DEFINE_KEYCODE(MACRO_3), \
     DEFINE_KEYCODE(MACRO_4), \
     DEFINE_KEYCODE(EMOJI_PICKER), \
-    DEFINE_KEYCODE(SCREENSHOT)
+    DEFINE_KEYCODE(SCREENSHOT), \
+    DEFINE_KEYCODE(DICTATE), \
+    DEFINE_KEYCODE(NEW), \
+    DEFINE_KEYCODE(CLOSE), \
+    DEFINE_KEYCODE(DO_NOT_DISTURB), \
+    DEFINE_KEYCODE(PRINT), \
+    DEFINE_KEYCODE(LOCK), \
+    DEFINE_KEYCODE(FULLSCREEN), \
+    DEFINE_KEYCODE(F13), \
+    DEFINE_KEYCODE(F14), \
+    DEFINE_KEYCODE(F15), \
+    DEFINE_KEYCODE(F16), \
+    DEFINE_KEYCODE(F17), \
+    DEFINE_KEYCODE(F18), \
+    DEFINE_KEYCODE(F19),\
+    DEFINE_KEYCODE(F20), \
+    DEFINE_KEYCODE(F21), \
+    DEFINE_KEYCODE(F22), \
+    DEFINE_KEYCODE(F23), \
+    DEFINE_KEYCODE(F24)
 
 // NOTE: If you add a new axis here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index eddd568..797efbe 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -306,7 +306,18 @@
         }
         if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
             mRequiredPermission = hwSensor.requiredPermission;
-            if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
+            bool requiresBodySensorPermission =
+                    !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS);
+            if (android::permission::flags::replace_body_sensor_permission_enabled()) {
+                if (requiresBodySensorPermission) {
+                  ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str());
+                }
+
+                AppOpsManager appOps;
+                // Lookup to see if an AppOp exists for the permission. If none
+                // does, the default value of -1 is used.
+                mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
+            } else if (requiresBodySensorPermission) {
                 AppOpsManager appOps;
                 mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
             }
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 7dff144..ca797dc 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -53,13 +53,13 @@
 }
 
 void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
-                                    int32_t resolution) {
+                                    int32_t resolution, int32_t flat, int32_t fuzz) {
     EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
             .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
                                                    .minValue = min,
                                                    .maxValue = max,
-                                                   .flat = 0,
-                                                   .fuzz = 0,
+                                                   .flat = flat,
+                                                   .fuzz = fuzz,
                                                    .resolution = resolution,
                                            }}
                                          : std::nullopt));
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index fc27e4f..b6c5812 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -43,7 +43,8 @@
     virtual void SetUp() override { SetUpWithBus(0); }
     virtual void SetUpWithBus(int bus);
 
-    void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
+    void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution,
+                   int32_t flat = 0, int32_t fuzz = 0);
 
     void expectScanCodes(bool present, std::set<int> scanCodes);
 
diff --git a/services/inputflinger/tests/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp
index 01814a6..2c12653 100644
--- a/services/inputflinger/tests/SensorInputMapper_test.cpp
+++ b/services/inputflinger/tests/SensorInputMapper_test.cpp
@@ -16,168 +16,164 @@
 
 #include "SensorInputMapper.h"
 
+#include <cstdint>
+#include <list>
+#include <optional>
+#include <utility>
 #include <vector>
 
 #include <EventHub.h>
 #include <NotifyArgs.h>
+#include <ftl/enum.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
+#include <input/PrintTools.h>
 #include <linux/input-event-codes.h>
 
 #include "InputMapperTest.h"
+#include "TestEventMatchers.h"
 
 namespace android {
 
-class SensorInputMapperTest : public InputMapperTest {
+using testing::AllOf;
+using testing::ElementsAre;
+using testing::Return;
+using testing::VariantWith;
+
+namespace {
+
+constexpr int32_t ACCEL_RAW_MIN = -32768;
+constexpr int32_t ACCEL_RAW_MAX = 32768;
+constexpr int32_t ACCEL_RAW_FUZZ = 16;
+constexpr int32_t ACCEL_RAW_FLAT = 0;
+constexpr int32_t ACCEL_RAW_RESOLUTION = 8192;
+
+constexpr int32_t GYRO_RAW_MIN = -2097152;
+constexpr int32_t GYRO_RAW_MAX = 2097152;
+constexpr int32_t GYRO_RAW_FUZZ = 16;
+constexpr int32_t GYRO_RAW_FLAT = 0;
+constexpr int32_t GYRO_RAW_RESOLUTION = 1024;
+
+constexpr float GRAVITY_MS2_UNIT = 9.80665f;
+constexpr float DEGREE_RADIAN_UNIT = 0.0174533f;
+
+} // namespace
+
+class SensorInputMapperTest : public InputMapperUnitTest {
 protected:
-    static const int32_t ACCEL_RAW_MIN;
-    static const int32_t ACCEL_RAW_MAX;
-    static const int32_t ACCEL_RAW_FUZZ;
-    static const int32_t ACCEL_RAW_FLAT;
-    static const int32_t ACCEL_RAW_RESOLUTION;
+    void SetUp() override {
+        InputMapperUnitTest::SetUp();
+        EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+                .WillRepeatedly(Return(InputDeviceClass::SENSOR));
+        // The mapper requests info on all ABS axes, including ones which aren't actually used, so
+        // just return nullopt for all axes we don't explicitly set up.
+        EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_))
+                .WillRepeatedly(Return(std::nullopt));
+    }
 
-    static const int32_t GYRO_RAW_MIN;
-    static const int32_t GYRO_RAW_MAX;
-    static const int32_t GYRO_RAW_FUZZ;
-    static const int32_t GYRO_RAW_FLAT;
-    static const int32_t GYRO_RAW_RESOLUTION;
-
-    static const float GRAVITY_MS2_UNIT;
-    static const float DEGREE_RADIAN_UNIT;
-
-    void prepareAccelAxes();
-    void prepareGyroAxes();
-    void setAccelProperties();
-    void setGyroProperties();
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); }
+    void setupSensor(int32_t absCode, InputDeviceSensorType type, int32_t sensorDataIndex) {
+        EXPECT_CALL(mMockEventHub, mapSensor(EVENTHUB_ID, absCode))
+                .WillRepeatedly(Return(std::make_pair(type, sensorDataIndex)));
+    }
 };
 
-const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768;
-const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768;
-const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16;
-const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0;
-const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192;
-
-const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152;
-const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152;
-const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16;
-const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0;
-const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024;
-
-const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f;
-const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f;
-
-void SensorInputMapperTest::prepareAccelAxes() {
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
-                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
-                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
-                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
-}
-
-void SensorInputMapperTest::prepareGyroAxes() {
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
-                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
-                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
-    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
-                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
-}
-
-void SensorInputMapperTest::setAccelProperties() {
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER,
-                                 /* sensorDataIndex */ 0);
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER,
-                                 /* sensorDataIndex */ 1);
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER,
-                                 /* sensorDataIndex */ 2);
-    mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
-    addConfigurationProperty("sensor.accelerometer.reportingMode", "0");
-    addConfigurationProperty("sensor.accelerometer.maxDelay", "100000");
-    addConfigurationProperty("sensor.accelerometer.minDelay", "5000");
-    addConfigurationProperty("sensor.accelerometer.power", "1.5");
-}
-
-void SensorInputMapperTest::setGyroProperties() {
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE,
-                                 /* sensorDataIndex */ 0);
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE,
-                                 /* sensorDataIndex */ 1);
-    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE,
-                                 /* sensorDataIndex */ 2);
-    mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
-    addConfigurationProperty("sensor.gyroscope.reportingMode", "0");
-    addConfigurationProperty("sensor.gyroscope.maxDelay", "100000");
-    addConfigurationProperty("sensor.gyroscope.minDelay", "5000");
-    addConfigurationProperty("sensor.gyroscope.power", "0.8");
-}
-
 TEST_F(SensorInputMapperTest, GetSources) {
-    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
+    mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+                                                   mFakePolicy->getReaderConfiguration());
 
-    ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
+    ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mMapper->getSources());
 }
 
 TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
-    setAccelProperties();
-    prepareAccelAxes();
-    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
+    EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP))
+            .WillRepeatedly(Return(true));
+    setupSensor(ABS_X, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/0);
+    setupSensor(ABS_Y, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/1);
+    setupSensor(ABS_Z, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/2);
+    setupAxis(ABS_X, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+              ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+    setupAxis(ABS_Y, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+              ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+    setupAxis(ABS_Z, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+              ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+    mPropertyMap.addProperty("sensor.accelerometer.reportingMode", "0");
+    mPropertyMap.addProperty("sensor.accelerometer.maxDelay", "100000");
+    mPropertyMap.addProperty("sensor.accelerometer.minDelay", "5000");
+    mPropertyMap.addProperty("sensor.accelerometer.power", "1.5");
+    mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+                                                   mFakePolicy->getReaderConfiguration());
 
-    ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
-                                    std::chrono::microseconds(10000),
-                                    std::chrono::microseconds(0)));
-    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID));
+    ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::ACCELEROMETER,
+                                      std::chrono::microseconds(10000),
+                                      std::chrono::microseconds(0)));
+    std::list<NotifyArgs> args;
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
+    args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
-    NotifySensorArgs args;
     std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
                                  -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
                                  40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT};
 
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
-    ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
-    ASSERT_EQ(args.deviceId, DEVICE_ID);
-    ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER);
-    ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
-    ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
-    ASSERT_EQ(args.values, values);
-    mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER);
+    ASSERT_EQ(args.size(), 1u);
+    const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front());
+    ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
+    ASSERT_EQ(arg.deviceId, DEVICE_ID);
+    ASSERT_EQ(arg.sensorType, InputDeviceSensorType::ACCELEROMETER);
+    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
+    ASSERT_EQ(arg.values, values);
+    mMapper->flushSensor(InputDeviceSensorType::ACCELEROMETER);
 }
 
 TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
-    setGyroProperties();
-    prepareGyroAxes();
-    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
+    EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP))
+            .WillRepeatedly(Return(true));
+    setupSensor(ABS_RX, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/0);
+    setupSensor(ABS_RY, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/1);
+    setupSensor(ABS_RZ, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/2);
+    setupAxis(ABS_RX, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+              GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+    setupAxis(ABS_RY, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+              GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+    setupAxis(ABS_RZ, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+              GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+    mPropertyMap.addProperty("sensor.gyroscope.reportingMode", "0");
+    mPropertyMap.addProperty("sensor.gyroscope.maxDelay", "100000");
+    mPropertyMap.addProperty("sensor.gyroscope.minDelay", "5000");
+    mPropertyMap.addProperty("sensor.gyroscope.power", "0.8");
+    mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+                                                   mFakePolicy->getReaderConfiguration());
 
-    ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
-                                    std::chrono::microseconds(10000),
-                                    std::chrono::microseconds(0)));
-    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID));
+    ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::GYROSCOPE,
+                                      std::chrono::microseconds(10000),
+                                      std::chrono::microseconds(0)));
+    std::list<NotifyArgs> args;
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
+    args += process(ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
+    args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
-    NotifySensorArgs args;
     std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
                                  -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
                                  40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT};
 
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
-    ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
-    ASSERT_EQ(args.deviceId, DEVICE_ID);
-    ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE);
-    ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
-    ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
-    ASSERT_EQ(args.values, values);
-    mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
+    ASSERT_EQ(args.size(), 1u);
+    const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front());
+    ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
+    ASSERT_EQ(arg.deviceId, DEVICE_ID);
+    ASSERT_EQ(arg.sensorType, InputDeviceSensorType::GYROSCOPE);
+    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
+    ASSERT_EQ(arg.values, values);
+    mMapper->flushSensor(InputDeviceSensorType::GYROSCOPE);
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 8067ace..82eafd4 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,6 +42,7 @@
         "libutils",
     ],
     static_libs: [
+        "libguiflags",
         "libmath",
         "librenderengine",
         "libtimestats",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 98bccd8..252adaa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -46,6 +46,12 @@
     // content.
     bool isProtected = false;
 
+    // True if this display has picture processing hardware and pipelines.
+    bool hasPictureProcessing = false;
+
+    // The number of layer-specific picture-processing pipelines.
+    int32_t maxLayerPictureProfiles = 0;
+
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
     adpf::PowerAdvisor* powerAdvisor = nullptr;
@@ -82,6 +88,16 @@
         return *this;
     }
 
+    DisplayCreationArgsBuilder& setHasPictureProcessing(bool hasPictureProcessing) {
+        mArgs.hasPictureProcessing = hasPictureProcessing;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setMaxLayerPictureProfiles(int32_t maxLayerPictureProfiles) {
+        mArgs.maxLayerPictureProfiles = maxLayerPictureProfiles;
+        return *this;
+    }
+
     DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 4e080b3..a5e9dde 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -148,9 +148,6 @@
     virtual std::optional<LayerSettings> prepareClientComposition(
             ClientCompositionTargetSettings&) const = 0;
 
-    // Called after the layer is displayed to update the presentation fence
-    virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
-
     // Initializes a promise for a buffer release fence and provides the future for that
     // fence. This should only be called when a promise has not yet been created, or
     // after the previous promise has already been fulfilled. Attempting to call this
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a91a07..fb8fed0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -25,6 +25,7 @@
 #include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/ShadowSettings.h>
@@ -219,6 +220,14 @@
     float currentHdrSdrRatio = 1.f;
     float desiredHdrSdrRatio = 1.f;
 
+    // A picture profile handle refers to a PictureProfile configured on the display, which is a
+    // set of parameters that configures the picture processing hardware that is used to enhance
+    // the quality of buffer contents.
+    PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+    // A layer's priority in terms of limited picture processing pipeline utilization.
+    int64_t pictureProfilePriority;
+
     gui::CachingHint cachingHint = gui::CachingHint::Enabled;
 
     std::shared_ptr<gui::DisplayLuts> luts;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 556aa24..bda7856 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <ftl/future.h>
+#include <ftl/optional.h>
 #include <cstdint>
 #include <iterator>
 #include <optional>
@@ -26,18 +28,18 @@
 #include <vector>
 
 #include <compositionengine/LayerFE.h>
-#include <ftl/future.h>
 #include <renderengine/LayerSettings.h>
+#include <ui/DisplayIdentification.h>
 #include <ui/Fence.h>
 #include <ui/FenceTime.h>
 #include <ui/GraphicTypes.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
-#include <ui/DisplayIdentification.h>
 #include "DisplayHardware/HWComposer.h"
 
 namespace android {
@@ -167,7 +169,7 @@
     virtual bool isValid() const = 0;
 
     // Returns the DisplayId the output represents, if it has one
-    virtual std::optional<DisplayId> getDisplayId() const = 0;
+    virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
 
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
@@ -331,6 +333,9 @@
     virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
     virtual const aidl::android::hardware::graphics::composer3::OverlayProperties*
     getOverlaySupport() = 0;
+    virtual bool hasPictureProcessing() const = 0;
+    virtual int32_t getMaxLayerPictureProfiles() const = 0;
+    virtual void applyPictureProfile() = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 80c5124..2e7a7d9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <ui/PictureProfileHandle.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 
@@ -86,6 +87,16 @@
     // longer cares about.
     virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
 
+    // Get the relative priority of the layer's picture profile with respect to the importance of
+    // the visual content to the user experience. Lower is higher priority.
+    virtual int64_t getPictureProfilePriority() const = 0;
+
+    // The picture profile handle for the layer.
+    virtual const PictureProfileHandle& getPictureProfileHandle() const = 0;
+
+    // Commit the picture profile to the composition state.
+    virtual void commitPictureProfileToCompositionState() = 0;
+
     // Recalculates the state of the output layer from the output-independent
     // layer. If includeGeometry is false, the geometry state can be skipped.
     // internalDisplayRotationFlags must be set to the rotation flags for the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 104a61b..5519aaf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -45,7 +45,7 @@
     virtual ~Display();
 
     // compositionengine::Output overrides
-    std::optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayId> getDisplayId() const override;
     bool isValid() const override;
     void dump(std::string&) const override;
     using compositionengine::impl::Output::setReleasedLayers;
@@ -100,11 +100,16 @@
     void setHintSessionGpuStart(TimePoint startTime) override;
     void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
     void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
+    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
+            override;
+    bool hasPictureProcessing() const override;
+    int32_t getMaxLayerPictureProfiles() const override;
+
     DisplayId mId;
     bool mIsDisconnected = false;
     adpf::PowerAdvisor* mPowerAdvisor = nullptr;
-    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
-            override;
+    bool mHasPictureProcessing = false;
+    int32_t mMaxLayerPictureProfiles = 0;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 69e1efc..0ccdd22 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,6 +16,11 @@
 
 #pragma once
 
+#include <ftl/optional.h>
+#include <memory>
+#include <utility>
+#include <vector>
+
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
@@ -28,10 +33,6 @@
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
 
-#include <memory>
-#include <utility>
-#include <vector>
-
 namespace android::compositionengine::impl {
 
 // The implementation class contains the common implementation, but does not
@@ -43,7 +44,7 @@
 
     // compositionengine::Output overrides
     bool isValid() const override;
-    std::optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
     void setLayerCachingTexturePoolEnabled(bool) override;
@@ -84,13 +85,14 @@
     bool supportsOffloadPresent() const override { return false; }
     void offloadPresentNextFrame() override;
 
-    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
     void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
     void collectVisibleLayers(const CompositionRefreshArgs&,
                               compositionengine::Output::CoverageState&) override;
     void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
                                     compositionengine::Output::CoverageState&) override;
     void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+    void commitPictureProfilesToCompositionState();
 
     void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void planComposition() override;
@@ -151,6 +153,9 @@
     void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
     bool isPowerHintSessionEnabled() override;
     bool isPowerHintSessionGpuReportingEnabled() override;
+    bool hasPictureProcessing() const override;
+    int32_t getMaxLayerPictureProfiles() const override;
+    void applyPictureProfile() override;
     void dumpBase(std::string&) const;
 
     // Implemented by the final implementation for the final state it uses.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f8ffde1..c76b344 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -35,6 +35,7 @@
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/ProjectionSpace.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -170,6 +171,8 @@
 
     ICEPowerCallback* powerCallback = nullptr;
 
+    PictureProfileHandle pictureProfileHandle;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index bfa387d..712b551 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -25,6 +25,7 @@
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/OutputLayer.h>
 #include <ui/FloatRect.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 
 #include <ui/DisplayIdentification.h>
@@ -48,6 +49,9 @@
     void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
     void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+    int64_t getPictureProfilePriority() const override;
+    const PictureProfileHandle& getPictureProfileHandle() const override;
+    void commitPictureProfileToCompositionState() override;
 
     void updateCompositionState(bool includeGeometry, bool forceClientComposition,
                                 ui::Transform::RotationFlags,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 28216a4..c558739 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -22,6 +22,7 @@
 #include <renderengine/ExternalTexture.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -101,6 +102,9 @@
     // order to save power.
     Region outputSpaceBlockingRegionHint;
 
+    // The picture profile for this layer.
+    PictureProfileHandle pictureProfileHandle;
+
     // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
     struct {
         std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 05a5d38..c7ff704 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -50,9 +50,6 @@
                        std::optional<compositionengine::LayerFE::LayerSettings>(
                                compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
-    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
-                (override));
-
     MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
     MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
     MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 33cdc54..f2c265a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -34,7 +34,7 @@
     virtual ~Output();
 
     MOCK_CONST_METHOD0(isValid, bool());
-    MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
+    MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
@@ -142,6 +142,9 @@
     MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ());
     MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*),
                 getOverlaySupport, ());
+    MOCK_METHOD(bool, hasPictureProcessing, (), (const));
+    MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (), (const));
+    MOCK_METHOD(void, applyPictureProfile, ());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 12f2094..9333ebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -63,7 +63,9 @@
                 (ndk::ScopedFileDescriptor,
                  (std::vector<std::pair<
                           int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
-
+    MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
+    MOCK_METHOD(const PictureProfileHandle&, getPictureProfileHandle, (), (const));
+    MOCK_METHOD(void, commitPictureProfileToCompositionState, ());
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6a97c49..e37ce0a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -54,6 +54,8 @@
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mId = args.id;
     mPowerAdvisor = args.powerAdvisor;
+    mHasPictureProcessing = args.hasPictureProcessing;
+    mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
     editState().isSecure = args.isSecure;
     editState().isProtected = args.isProtected;
     editState().displaySpace.setBounds(args.pixels);
@@ -80,7 +82,7 @@
     return mId.isVirtual();
 }
 
-std::optional<DisplayId> Display::getDisplayId() const {
+ftl::Optional<DisplayId> Display::getDisplayId() const {
     return mId;
 }
 
@@ -203,15 +205,16 @@
 }
 
 void Display::applyDisplayBrightness(bool applyImmediately) {
-    if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast);
-        displayId && getState().displayBrightness) {
+    if (!getState().displayBrightness) {
+        return;
+    }
+    if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
-        const status_t result =
-                hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
-                                         getState().displayBrightnessNits,
-                                         Hwc2::Composer::DisplayBrightnessOptions{
-                                                 .applyImmediately = applyImmediately})
-                        .get();
+        status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
+                                                   getState().displayBrightnessNits,
+                                                   Hwc2::Composer::DisplayBrightnessOptions{
+                                                           .applyImmediately = applyImmediately})
+                                  .get();
         ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
                  getName().c_str(), result, strerror(-result));
     }
@@ -288,8 +291,8 @@
 }
 
 bool Display::getSkipColorTransform() const {
-    const auto& hwc = getCompositionEngine().getHwComposer();
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
         return hwc.hasDisplayCapability(*halDisplayId,
                                         DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
@@ -462,6 +465,14 @@
     return &getCompositionEngine().getHwComposer().getOverlaySupport();
 }
 
+bool Display::hasPictureProcessing() const {
+    return mHasPictureProcessing;
+}
+
+int32_t Display::getMaxLayerPictureProfiles() const {
+    return mMaxLayerPictureProfiles;
+}
+
 void Display::finishFrame(GpuCompositionResult&& result) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
@@ -476,8 +487,8 @@
 }
 
 bool Display::supportsOffloadPresent() const {
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
-        const auto& hwc = getCompositionEngine().getHwComposer();
+    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+        auto& hwc = getCompositionEngine().getHwComposer();
         return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 2d10866..348111d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -127,6 +127,9 @@
     }
     dumpVal(out, "colorTransform", colorTransform);
     dumpVal(out, "caching hint", toString(cachingHint));
+    if (pictureProfileHandle) {
+        dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+    }
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index bb45613..98b6666 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -32,9 +32,12 @@
 #include <compositionengine/impl/planner/Planner.h>
 #include <ftl/algorithm.h>
 #include <ftl/future.h>
+#include <ftl/optional.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/Time.h>
 
+#include <com_android_graphics_libgui_flags.h>
+
 #include <optional>
 #include <thread>
 
@@ -111,7 +114,7 @@
             mRenderSurface->isValid();
 }
 
-std::optional<DisplayId> Output::getDisplayId() const {
+ftl::Optional<DisplayId> Output::getDisplayId() const {
     return {};
 }
 
@@ -433,7 +436,7 @@
 ftl::Future<std::monostate> Output::present(
         const compositionengine::CompositionRefreshArgs& refreshArgs) {
     const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
-        return ftl::Optional(getDisplayId())
+        return getDisplayId()
                 .and_then(PhysicalDisplayId::tryCast)
                 .and_then([&refreshArgs](PhysicalDisplayId id) {
                     return refreshArgs.frameTargets.get(id);
@@ -500,15 +503,6 @@
     updateHwcAsyncWorker();
 }
 
-void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
-    if (bufferIdsToUncache.empty()) {
-        return;
-    }
-    for (auto outputLayer : getOutputLayersOrderedByZ()) {
-        outputLayer->uncacheBuffers(bufferIdsToUncache);
-    }
-}
-
 void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
                                 LayerFESet& layerFESet) {
     auto& outputState = editState();
@@ -776,11 +770,11 @@
 
     // The layer is visible. Either reuse the existing outputLayer if we have
     // one, or create a new one if we do not.
-    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
+    auto outputLayer = ensureOutputLayer(prevOutputLayerIndex, layerFE);
 
     // Store the layer coverage information into the layer state as some of it
     // is useful later.
-    auto& outputLayerState = result->editState();
+    auto& outputLayerState = outputLayer->editState();
     outputLayerState.visibleRegion = visibleRegion;
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
@@ -798,6 +792,52 @@
     }
 }
 
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+    if (bufferIdsToUncache.empty()) {
+        return;
+    }
+    for (auto outputLayer : getOutputLayersOrderedByZ()) {
+        outputLayer->uncacheBuffers(bufferIdsToUncache);
+    }
+}
+
+void Output::commitPictureProfilesToCompositionState() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+    if (!hasPictureProcessing()) {
+        return;
+    }
+    auto compare = [](const ::android::compositionengine::OutputLayer* lhs,
+                      const ::android::compositionengine::OutputLayer* rhs) {
+        return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority();
+    };
+    std::priority_queue<::android::compositionengine::OutputLayer*,
+                        std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)>
+            layersWithProfiles;
+    for (auto outputLayer : getOutputLayersOrderedByZ()) {
+        if (outputLayer->getPictureProfileHandle()) {
+            layersWithProfiles.push(outputLayer);
+        }
+    }
+
+    // TODO(b/337330263): Use the default display picture profile from SurfaceFlinger
+    editState().pictureProfileHandle = PictureProfileHandle::NONE;
+
+    // When layer-specific picture processing is supported, apply as many high priority profiles as
+    // possible to the layers, and ignore the low priority layers.
+    if (getMaxLayerPictureProfiles() > 0) {
+        for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty();
+             layersWithProfiles.pop(), ++i) {
+            layersWithProfiles.top()->commitPictureProfileToCompositionState();
+        }
+        // No layer-specific picture processing, so apply the highest priority picture profile to
+        // the entire display.
+    } else if (!layersWithProfiles.empty()) {
+        editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle();
+    }
+}
+
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
     // The base class does nothing with this call.
 }
@@ -826,6 +866,7 @@
             forceClientComposition = false;
         }
     }
+    commitPictureProfilesToCompositionState();
 }
 
 void Output::planComposition() {
@@ -847,7 +888,7 @@
         return;
     }
 
-    if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId())
+    if (auto frameTargetPtrOpt = getDisplayId()
                                          .and_then(PhysicalDisplayId::tryCast)
                                          .and_then([&refreshArgs](PhysicalDisplayId id) {
                                              return refreshArgs.frameTargets.get(id);
@@ -858,6 +899,8 @@
     editState().frameInterval = refreshArgs.frameInterval;
     editState().powerCallback = refreshArgs.powerCallback;
 
+    applyPictureProfile();
+
     compositionengine::OutputLayer* peekThroughLayer = nullptr;
     sp<GraphicBuffer> previousOverride = nullptr;
     bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -1763,5 +1806,34 @@
     return getState().displayBrightnessNits / getState().sdrWhitePointNits;
 }
 
+bool Output::hasPictureProcessing() const {
+    return false;
+}
+
+int32_t Output::getMaxLayerPictureProfiles() const {
+    return 0;
+}
+
+void Output::applyPictureProfile() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+
+    // TODO(b/337330263): Move this into the Display class and add a Display unit test.
+    if (!getState().pictureProfileHandle) {
+        return;
+    }
+    if (!getDisplayId()) {
+        return;
+    }
+    if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+        auto& hwc = getCompositionEngine().getHwComposer();
+        const status_t error =
+                hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
+        ALOGE_IF(error, "setDisplayPictureProfileHandle failed for %s: %d, (%s)", getName().c_str(),
+                 error, strerror(-error));
+    }
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index c062df3..f6d9a1a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <DisplayHardware/Hal.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/DisplayColorProfile.h>
@@ -22,11 +23,12 @@
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ui/FloatRect.h>
+#include <ui/HdrRenderTypeUtils.h>
 #include <cstdint>
 #include "system/graphics-base-v1.0.h"
-#include "ui/FloatRect.h"
 
-#include <ui/HdrRenderTypeUtils.h>
+#include <com_android_graphics_libgui_flags.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -424,6 +426,16 @@
     }
 }
 
+void OutputLayer::commitPictureProfileToCompositionState() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+    const auto* layerState = getLayerFE().getCompositionState();
+    if (layerState) {
+        editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle;
+    }
+}
+
 void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
                                   bool zIsOverridden, bool isPeekingThrough) {
     const auto& state = getState();
@@ -645,6 +657,21 @@
         ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
               dimmingRatio, to_string(error).c_str(), static_cast<int32_t>(error));
     }
+
+    if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+        outputDependentState.pictureProfileHandle) {
+        if (auto error =
+                    hwcLayer->setPictureProfileHandle(outputDependentState.pictureProfileHandle);
+            error != hal::Error::NONE) {
+            ALOGE("[%s] Failed to set picture profile handle: %s (%d)", getLayerFE().getDebugName(),
+                  toString(outputDependentState.pictureProfileHandle).c_str(),
+                  static_cast<int32_t>(error));
+        }
+        // Reset the picture profile state, as it needs to be re-committed on each present cycle
+        // when Output decides that the limited picture-processing hardware should be used by this
+        // layer.
+        editState().pictureProfileHandle = PictureProfileHandle::NONE;
+    }
 }
 
 void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
@@ -750,6 +777,16 @@
     }
 }
 
+int64_t OutputLayer::getPictureProfilePriority() const {
+    const auto* layerState = getLayerFE().getCompositionState();
+    return layerState ? layerState->pictureProfilePriority : 0;
+}
+
+const PictureProfileHandle& OutputLayer::getPictureProfileHandle() const {
+    const auto* layerState = getLayerFE().getCompositionState();
+    return layerState ? layerState->pictureProfileHandle : PictureProfileHandle::NONE;
+}
+
 void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
                                         const LayerFECompositionState& outputIndependentState,
                                         bool skipLayer) {
@@ -832,14 +869,14 @@
         return;
     }
 
-    const auto* layerFEState = getLayerFE().getCompositionState();
-    if (!layerFEState) {
+    const auto* layerState = getLayerFE().getCompositionState();
+    if (!layerState) {
         return;
     }
 
     const auto& outputState = getOutput().getState();
 
-    Rect frame = layerFEState->cursorFrame;
+    Rect frame = layerState->cursorFrame;
     frame.intersect(outputState.layerStackSpace.getContent(), &frame);
     Rect position = outputState.transform.transform(frame);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index da1f7e4..deef747 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,9 @@
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "whitePointNits", whitePointNits);
     dumpVal(out, "dimmingRatio", dimmingRatio);
+    if (pictureProfileHandle) {
+        dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+    }
     dumpVal(out, "override buffer", overrideInfo.buffer.get());
     dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
     dumpVal(out, "override display frame", overrideInfo.displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index f2c5672..dbffe80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <com_android_graphics_libgui_flags.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -1332,6 +1333,71 @@
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+    mOutputLayer.commitPictureProfileToCompositionState();
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+    mOutputLayer.commitPictureProfileToCompositionState();
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls(kExpectedHwcSlot, nullptr, kFence);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0);
+    // No committing of picture profile before writing the state
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
 /*
  * OutputLayer::uncacheBuffers
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fe7dd9a..99e68eb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/stringprintf.h>
+#include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
@@ -37,11 +38,14 @@
 #include <cstdint>
 #include <variant>
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 #include <common/FlagManager.h>
 #include <common/test/FlagUtils.h>
 #include "CallOrderStateMachineHelper.h"
 #include "RegionMatcher.h"
 #include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 namespace android::compositionengine {
 namespace {
@@ -142,6 +146,24 @@
     public:
         using impl::Output::injectOutputLayerForTest;
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+        virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+
+        virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
+        virtual int32_t getMaxLayerPictureProfiles() const override {
+            return mMaxLayerPictureProfiles;
+        }
+
+        void setDisplayIdForTest(DisplayId value) { mId = value; }
+
+        void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
+
+        void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
+
+    private:
+        ftl::Optional<DisplayId> mId;
+        bool mHasPictureProcessing;
+        int32_t mMaxLayerPictureProfiles;
     };
 
     static std::shared_ptr<Output> createOutput(
@@ -157,6 +179,7 @@
         mOutput->editState().displaySpace.setBounds(
                 ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
     }
 
     void injectOutputLayer(InjectedLayer& layer) {
@@ -169,6 +192,7 @@
 
     static const Rect kDefaultDisplaySize;
 
+    StrictMock<::android::mock::HWComposer> mHwComposer;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
@@ -5043,6 +5067,123 @@
     mOutput->writeCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has only one display-global picture processing pipeline
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(0);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // No layer picture profiles should be committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0);
+
+    // Sets display picture profile to the highest priority layer's profile
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has 2 layer-specific picture processing pipelines
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(2);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // The two highest priority layers should have their picture profiles committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
+
+    // No display picture profile is sent
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
     // In split-screen landscape mode, the screen is rotated 90 degrees, with
     // one layer on the left covering the left side of the output, and one layer
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index a346981..231b40b 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,7 +27,6 @@
 #include "LayerFE.h"
 #include "SurfaceFlinger.h"
 #include "ui/FenceResult.h"
-#include "ui/LayerStack.h"
 
 namespace android {
 
@@ -343,11 +342,6 @@
     caster.shadow = state;
 }
 
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
-                               ui::LayerStack layerStack) {
-    mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
-}
-
 CompositionResult&& LayerFE::stealCompositionResult() {
     return std::move(mCompositionResult);
 }
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 658f949..5081e10 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -22,14 +22,12 @@
 #include "compositionengine/LayerFE.h"
 #include "compositionengine/LayerFECompositionState.h"
 #include "renderengine/LayerSettings.h"
-#include "ui/LayerStack.h"
 
 #include <ftl/future.h>
 
 namespace android {
 
 struct CompositionResult {
-    std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
     sp<Fence> lastClientCompositionFence = nullptr;
 };
 
@@ -41,7 +39,6 @@
     // compositionengine::LayerFE overrides
     const compositionengine::LayerFECompositionState* getCompositionState() const override;
     bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
     const char* getDebugName() const override;
     int32_t getSequence() const override;
     bool hasRoundedCorners() const override;
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index e6fed63..7b6e4bf 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -341,9 +341,9 @@
     WindowInfosListenerUtils windowInfosListenerUtils;
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                            ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -370,7 +370,8 @@
         UIDFaker f(AID_SYSTEM);
         auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
             auto foundWindowInfo =
-                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+                    WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+                                                                     windowInfos);
             if (!foundWindowInfo) {
                 return false;
             }
@@ -386,7 +387,8 @@
         Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true);
         auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
             auto foundWindowInfo =
-                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+                    WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+                                                                     windowInfos);
             if (!foundWindowInfo) {
                 return false;
             }
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index ad9a674..2dd0dd9 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -50,9 +50,9 @@
 TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -65,14 +65,14 @@
             .apply();
 
     auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return findMatchingWindowInfo(windowInfo, windowInfos);
+        return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
 
     Transaction().reparent(surfaceControl, nullptr).apply();
 
     auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return !findMatchingWindowInfo(windowInfo, windowInfos);
+        return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
 }
@@ -80,9 +80,9 @@
 TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -96,7 +96,7 @@
             .apply();
 
     auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
-        auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+        auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
         if (!foundWindowInfo) {
             return false;
         }
@@ -104,19 +104,19 @@
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
 
-    windowInfo.addTouchableRegion({0, 0, 50, 50});
+    windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50});
     Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
 
     auto windowIsPresentAndTouchableRegionMatches =
             [&](const std::vector<WindowInfo>& windowInfos) {
-                auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+                auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
                 if (!foundWindowInfo) {
                     return false;
                 }
 
                 auto touchableRegion =
                         foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion);
-                return touchableRegion.hasSameRects(windowInfo.touchableRegion);
+                return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion);
             };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
 }