Merge "Allow apps to apply picture profiles with priority to layers" 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/Android.bp b/libs/gui/Android.bp
index 80e148b..1e33abb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -274,6 +274,7 @@
         "LayerMetadata.cpp",
         "LayerStatePermissions.cpp",
         "LayerState.cpp",
+        "DisplayLuts.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
         "ScreenCaptureResults.cpp",
diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp
new file mode 100644
index 0000000..8042976
--- /dev/null
+++ b/libs/gui/DisplayLuts.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/gui/DisplayLuts.h"
+#include <gui/DisplayLuts.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    SAFE_PARCEL(parcel->readInt32, &dimension);
+    SAFE_PARCEL(parcel->readInt32, &size);
+    SAFE_PARCEL(parcel->readInt32, &samplingKey);
+
+    return OK;
+}
+
+status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    SAFE_PARCEL(parcel->writeInt32, dimension);
+    SAFE_PARCEL(parcel->writeInt32, size);
+    SAFE_PARCEL(parcel->writeInt32, samplingKey);
+
+    return OK;
+}
+
+status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd);
+    SAFE_PARCEL(parcel->readInt32Vector, &offsets);
+    int32_t numLutProperties;
+    SAFE_PARCEL(parcel->readInt32, &numLutProperties);
+    lutProperties.reserve(numLutProperties);
+    for (int32_t i = 0; i < numLutProperties; i++) {
+        lutProperties.push_back({});
+        SAFE_PARCEL(lutProperties.back().readFromParcel, parcel);
+    }
+    return OK;
+}
+
+status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd);
+    SAFE_PARCEL(parcel->writeInt32Vector, offsets);
+    SAFE_PARCEL(parcel->writeInt32, static_cast<int32_t>(lutProperties.size()));
+    for (auto& entry : lutProperties) {
+        SAFE_PARCEL(entry.writeToParcel, parcel);
+    }
+    return OK;
+}
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 1c527d2..c1a03fc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -210,6 +210,12 @@
     SAFE_PARCEL(output.writeInt32, appContentPriority);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
 
+    const bool hasLuts = (luts != nullptr);
+    SAFE_PARCEL(output.writeBool, hasLuts);
+    if (hasLuts) {
+        SAFE_PARCEL(output.writeParcelable, *luts);
+    }
+
     return NO_ERROR;
 }
 
@@ -371,6 +377,15 @@
     SAFE_PARCEL(input.readInt32, &appContentPriority);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
 
+    bool hasLuts;
+    SAFE_PARCEL(input.readBool, &hasLuts);
+    if (hasLuts) {
+        luts = std::make_shared<gui::DisplayLuts>();
+        SAFE_PARCEL(input.readParcelable, luts.get());
+    } else {
+        luts = nullptr;
+    }
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index ebed576..61aabaa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1970,9 +1970,13 @@
         return *this;
     }
 
-    s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
-                                                 dimensions, sizes, samplingKeys);
     s->what |= layer_state_t::eLutsChanged;
+    if (lutFd.ok()) {
+        s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
+                                                     dimensions, sizes, samplingKeys);
+    } else {
+        s->luts = nullptr;
+    }
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -2128,13 +2132,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/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index 16a360d..ab86ac4 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -16,16 +16,24 @@
 #pragma once
 
 #include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
 #include <vector>
 
 namespace android::gui {
 
-struct DisplayLuts {
+struct DisplayLuts : public Parcelable {
 public:
-    struct Entry {
+    struct Entry : public Parcelable {
+        Entry() {};
+        Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey)
+              : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {}
         int32_t dimension;
         int32_t size;
         int32_t samplingKey;
+
+        status_t writeToParcel(android::Parcel* parcel) const override;
+        status_t readFromParcel(const android::Parcel* parcel) override;
     };
 
     DisplayLuts() {}
@@ -42,7 +50,10 @@
         }
     }
 
-    base::unique_fd& getLutFileDescriptor() { return fd; }
+    status_t writeToParcel(android::Parcel* parcel) const override;
+    status_t readFromParcel(const android::Parcel* parcel) override;
+
+    const base::unique_fd& getLutFileDescriptor() const { return fd; }
 
     std::vector<Entry> lutProperties;
     std::vector<int32_t> offsets;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6968d25..0d7f8c2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -688,7 +688,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/renderengine/Android.bp b/libs/renderengine/Android.bp
index d248ea0..7f207f0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@
         "skia/filters/KawaseBlurDualFilter.cpp",
         "skia/filters/KawaseBlurFilter.cpp",
         "skia/filters/LinearEffect.cpp",
+        "skia/filters/LutShader.cpp",
         "skia/filters/MouriMap.cpp",
         "skia/filters/StretchShaderFactory.cpp",
         "skia/filters/EdgeExtensionShaderFactory.cpp",
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index ec9d3ef..5c46c91 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -543,6 +543,10 @@
         }
     }
 
+    if (graphicBuffer && parameters.layer.luts) {
+        shader = mLutShader.lutShader(shader, parameters.layer.luts);
+    }
+
     if (parameters.requiresLinearEffect) {
         const auto format = targetBuffer != nullptr
                 ? std::optional<ui::PixelFormat>(
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index b5f8898..7be4c25 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,6 +39,7 @@
 #include "filters/BlurFilter.h"
 #include "filters/EdgeExtensionShaderFactory.h"
 #include "filters/LinearEffect.h"
+#include "filters/LutShader.h"
 #include "filters/StretchShaderFactory.h"
 
 class SkData;
@@ -184,6 +185,7 @@
 
     StretchShaderFactory mStretchShaderFactory;
     EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
+    LutShader mLutShader;
 
     sp<Fence> mLastDrawFence;
     BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
new file mode 100644
index 0000000..cea46ef
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "LutShader.h"
+
+#include <SkTileMode.h>
+#include <common/trace.h>
+#include <cutils/ashmem.h>
+#include <math/half.h>
+#include <sys/mman.h>
+
+#include "include/core/SkColorSpace.h"
+#include "src/core/SkColorFilterPriv.h"
+
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString kShader = SkString(R"(
+    uniform shader image;
+    uniform shader lut;
+    uniform int size;
+    uniform int key;
+    uniform int dimension;
+    vec4 main(vec2 xy) {
+        float4 rgba = image.eval(xy);
+        float3 linear = toLinearSrgb(rgba.rgb);
+        if (dimension == 1) {
+            // RGB
+            if (key == 0) {
+                float indexR = linear.r * float(size - 1);
+                float indexG = linear.g * float(size - 1);
+                float indexB = linear.b * float(size - 1);
+                float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r;
+                float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r;
+                float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r;
+                return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
+            // MAX_RGB
+            } else if (key == 1) {
+                float4 rgba = image.eval(xy);
+                float3 linear = toLinearSrgb(rgba.rgb);
+                float maxRGB = max(linear.r, max(linear.g, linear.b));
+                float index = maxRGB * float(size - 1);
+                float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+                return float4(linear * gain, rgba.a);
+            }
+        } else if (dimension == 3) {
+            if (key == 0) {
+                float tx = linear.r * float(size - 1);
+                float ty = linear.g * float(size - 1);
+                float tz = linear.b * float(size - 1);
+
+                // calculate lower and upper bounds for each dimension
+                int x = int(tx);
+                int y = int(ty);
+                int z = int(tz);
+
+                int i000 = x + y * size + z * size * size;
+                int i100 = i000 + 1;
+                int i010 = i000 + size;
+                int i110 = i000 + size + 1;
+                int i001 = i000 + size * size;
+                int i101 = i000 + size * size + 1;
+                int i011 = i000 + size * size + size;
+                int i111 = i000 + size * size + size + 1;
+
+                // get 1d normalized indices
+                float c000 = float(i000) / float(size * size * size);
+                float c100 = float(i100) / float(size * size * size);
+                float c010 = float(i010) / float(size * size * size);
+                float c110 = float(i110) / float(size * size * size);
+                float c001 = float(i001) / float(size * size * size);
+                float c101 = float(i101) / float(size * size * size);
+                float c011 = float(i011) / float(size * size * size);
+                float c111 = float(i111) / float(size * size * size);
+
+                //TODO(b/377984618): support Tetrahedral interpolation
+                // perform trilinear interpolation
+                float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb,
+                                 lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r);
+                float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb,
+                                 lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r);
+                float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb,
+                                 lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r);
+                float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb,
+                                 lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r);
+
+                float3 c0 = mix(c00, c10, linear.g);
+                float3 c1 = mix(c01, c11, linear.g);
+
+                float3 val = mix(c0, c1, linear.b);
+
+                return float4(val, rgba.a);
+            }
+        }
+        return rgba;
+    })");
+
+sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
+                                             const std::vector<float>& buffers,
+                                             const int32_t offset, const int32_t length,
+                                             const int32_t dimension, const int32_t size,
+                                             const int32_t samplingKey) {
+    SFTRACE_NAME("lut shader");
+    std::vector<half> buffer(length * 4); // 4 is for RGBA
+    auto d = static_cast<LutProperties::Dimension>(dimension);
+    if (d == LutProperties::Dimension::ONE_D) {
+        auto it = buffers.begin() + offset;
+        std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable {
+            float val = (i++ % 4 == 0) ? *it++ : 0.0f;
+            return half(val);
+        });
+    } else {
+        for (int i = 0; i < length; i++) {
+            buffer[i * 4] = half(buffers[offset + i]);
+            buffer[i * 4 + 1] = half(buffers[offset + length + i]);
+            buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]);
+            buffer[i * 4 + 3] = half(0);
+        }
+    }
+    /**
+     * 1D Lut(rgba)
+     * (R0, 0, 0, 0)
+     * (R1, 0, 0, 0)
+     * ...
+     *
+     * 3D Lut
+     * (R0, G0, B0, 0)
+     * (R1, G1, B1, 0)
+     * ...
+     */
+    SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1,
+                                         kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+    SkBitmap bitmap;
+    bitmap.allocPixels(info);
+    if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) {
+        LOG_ALWAYS_FATAL("unable to install pixels");
+    }
+
+    sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap);
+    mBuilder->child("image") = input;
+    mBuilder->child("lut") =
+            lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                    d == LutProperties::Dimension::ONE_D
+                                            ? SkSamplingOptions(SkFilterMode::kLinear)
+                                            : SkSamplingOptions());
+
+    const int uSize = static_cast<int>(size);
+    const int uKey = static_cast<int>(samplingKey);
+    const int uDimension = static_cast<int>(dimension);
+    mBuilder->uniform("size") = uSize;
+    mBuilder->uniform("key") = uKey;
+    mBuilder->uniform("dimension") = uDimension;
+    return mBuilder->makeShader();
+}
+
+sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
+                                     std::shared_ptr<gui::DisplayLuts> displayLuts) {
+    if (mBuilder == nullptr) {
+        const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader);
+        mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+    }
+
+    auto& fd = displayLuts->getLutFileDescriptor();
+    if (fd.ok()) {
+        // de-gamma the image without changing the primaries
+        SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+        if (baseImage) {
+            sk_sp<SkColorSpace> baseColorSpace =
+                    baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
+            sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
+            auto colorXformSdrToGainmap =
+                    SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace);
+            input = input->makeWithColorFilter(colorXformSdrToGainmap);
+        }
+
+        auto& offsets = displayLuts->offsets;
+        auto& lutProperties = displayLuts->lutProperties;
+        std::vector<float> buffers;
+        int fullLength = offsets[lutProperties.size() - 1];
+        if (lutProperties[lutProperties.size() - 1].dimension == 1) {
+            fullLength += lutProperties[lutProperties.size() - 1].size;
+        } else {
+            fullLength += (lutProperties[lutProperties.size() - 1].size *
+                           lutProperties[lutProperties.size() - 1].size *
+                           lutProperties[lutProperties.size() - 1].size * 3);
+        }
+        size_t bufferSize = fullLength * sizeof(float);
+
+        // decode the shared memory of luts
+        float* ptr =
+                (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+        if (ptr == MAP_FAILED) {
+            LOG_ALWAYS_FATAL("mmap failed");
+        }
+        buffers = std::vector<float>(ptr, ptr + fullLength);
+        munmap(ptr, bufferSize);
+
+        for (size_t i = 0; i < offsets.size(); i++) {
+            int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i]
+                                                             : offsets[i + 1] - offsets[i];
+            // divide by 3 for 3d Lut because of 3 (RGB) channels
+            if (static_cast<LutProperties::Dimension>(lutProperties[i].dimension) ==
+                LutProperties::Dimension::THREE_D) {
+                bufferSizePerLut /= 3;
+            }
+            input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut,
+                                      lutProperties[i].dimension, lutProperties[i].size,
+                                      lutProperties[i].samplingKey);
+        }
+
+        // re-gamma
+        baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+        if (baseImage) {
+            sk_sp<SkColorSpace> baseColorSpace =
+                    baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
+            sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
+            auto colorXformGainmapToDst =
+                    SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace);
+            input = input->makeWithColorFilter(colorXformGainmapToDst);
+        }
+    }
+    return input;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h
new file mode 100644
index 0000000..c157904
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *-
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <gui/DisplayLuts.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class LutShader {
+public:
+    sk_sp<SkShader> lutShader(sk_sp<SkShader>& input,
+                              std::shared_ptr<gui::DisplayLuts> displayLuts);
+
+private:
+    sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers,
+                                      const int32_t offset, const int32_t length,
+                                      const int32_t dimension, const int32_t size,
+                                      const int32_t samplingKey);
+    std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
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/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/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index cc491c2..712b551 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -105,7 +105,7 @@
             aidl::android::hardware::graphics::composer3::Composition from,
             aidl::android::hardware::graphics::composer3::Composition to) const;
     bool isClientCompositionForced(bool isPeekingThrough) const;
-    void updateLuts(std::shared_ptr<gui::DisplayLuts> luts,
+    void updateLuts(const LayerFECompositionState&,
                     const std::optional<std::vector<std::optional<LutProperties>>>& properties);
 };
 
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/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e31d684..f6d9a1a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -288,8 +288,13 @@
 }
 
 void OutputLayer::updateLuts(
-        std::shared_ptr<gui::DisplayLuts> layerFEStateLut,
+        const LayerFECompositionState& layerFEState,
         const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+    auto& luts = layerFEState.luts;
+    if (!luts) {
+        return;
+    }
+
     auto& state = editState();
 
     if (!properties) {
@@ -305,7 +310,7 @@
         }
     }
 
-    for (const auto& inputLut : layerFEStateLut->lutProperties) {
+    for (const auto& inputLut : luts->lutProperties) {
         bool foundInHwcLuts = false;
         for (const auto& hwcLut : hwcLutProperties) {
             if (static_cast<int32_t>(hwcLut.dimension) ==
@@ -318,7 +323,7 @@
                 break;
             }
         }
-        // if any lut properties of layerFEStateLut can not be found in hwcLutProperties,
+        // if any lut properties of luts can not be found in hwcLutProperties,
         // GPU composition instead
         if (!foundInHwcLuts) {
             state.forceClientComposition = true;
@@ -411,10 +416,7 @@
         state.whitePointNits = layerBrightnessNits;
     }
 
-    const auto& layerFEStateLut = layerFEState->luts;
-    if (layerFEStateLut) {
-        updateLuts(layerFEStateLut, properties);
-    }
+    updateLuts(*layerFEState, properties);
 
     // These are evaluated every frame as they can potentially change at any
     // time.
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 713a5c5..ee9302b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -163,7 +163,7 @@
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
     layer_state_t::merge(clientState);
     what = clientChanges;
-    LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
+    LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges);
 
     if (clientState.what & layer_state_t::eFlagsChanged) {
         if ((oldFlags ^ flags) &
@@ -633,7 +633,7 @@
             layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
             layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
             layer_state_t::eExtendedRangeBrightnessChanged |
-            layer_state_t::eDesiredHdrHeadroomChanged |
+            layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged |
             (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
                      ? layer_state_t::eFlagsChanged
                      : 0);
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));
 }