Merge "Remove setFrame from BufferStateLayer" into sc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 922367f..8bcb1e5 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/idlcli/
+               cmds/servicemanager/
                include/input/
                include/powermanager/
                libs/binder/fuzzer/
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 17ea903..a27fd10 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -86,7 +86,7 @@
                             bool generate_compact_dex,
                             bool use_jitzygote_image,
                             const char* compilation_reason) {
-    PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+    PrepareBootImageFlags(use_jitzygote_image);
 
     PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
                           dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,7 +112,7 @@
 
 RunDex2Oat::~RunDex2Oat() {}
 
-void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
     std::string boot_image;
     if (use_jitzygote_image) {
         boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
@@ -120,23 +120,6 @@
         boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
     }
     AddArg(boot_image);
-
-    // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
-    // BOOTCLASSPATH.
-    char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
-    if (dex2oat_bootclasspath != nullptr) {
-        AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
-    }
-
-    std::string updatable_bcp_packages =
-            MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
-                             "--updatable-bcp-packages-file=%s");
-    if (updatable_bcp_packages.empty()) {
-        // Make dex2oat fail by providing non-existent file name.
-        updatable_bcp_packages =
-                "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
-    }
-    AddArg(updatable_bcp_packages);
 }
 
 void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 325a3a2..475e124 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -56,7 +56,7 @@
     void Exec(int exit_code);
 
   protected:
-    void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+    void PrepareBootImageFlags(bool use_jitzygote_image);
     void PrepareInputFileFlags(const UniqueFile& output_oat,
                                const UniqueFile& output_vdex,
                                const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 3813cf7..0a638cd 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -175,8 +175,6 @@
         default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
-        default_expected_flags_["--updatable-bcp-packages-file"] =
-                "=/nonx/updatable-bcp-packages.txt";
 
         // Arch
         default_expected_flags_["--instruction-set"] = "=arm64";
@@ -320,28 +318,6 @@
     VerifyExpectedFlags();
 }
 
-TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
-    ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
-    ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
-        << "Failed to setenv: " << strerror(errno);
-
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
-    VerifyExpectedFlags();
-
-    ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
-        << "Failed to setenv: " << strerror(errno);
-}
-
-TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
-    setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
-    VerifyExpectedFlags();
-}
-
 TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
     auto args = RunDex2OatArgs::MakeDefaultTestArgs();
     args->generate_compact_dex = false;
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c47df52..c4ecd07 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1062,6 +1062,8 @@
 
 static const char* kProcFilesystems = "/proc/filesystems";
 bool supports_sdcardfs() {
+    if (!property_get_bool("external_storage.sdcardfs.enabled", true))
+        return false;
     std::string supported;
     if (!android::base::ReadFileToString(kProcFilesystems, &supported)) {
         PLOG(ERROR) << "Failed to read supported filesystems";
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index b6ff28d..7c1ca91 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -508,10 +508,10 @@
     EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6"));
     EXPECT_EQ("", err.str());
 
+    std::string error;
     vintf::HalManifest m;
-    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
-        << "--init-vintf does not emit valid HAL manifest: "
-        << vintf::gHalManifestConverter.lastError();
+    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str(), &error))
+        << "--init-vintf does not emit valid HAL manifest: " << error;
 }
 
 // test default columns
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 9de344a..3ebdeee 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -14,6 +14,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
 
     srcs: [
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 0dbab4e..2f55249 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -239,7 +239,8 @@
 #endif  // !VENDORSERVICEMANAGER
 
     // implicitly unlinked when the binder is removed
-    if (binder->remoteBinder() != nullptr && binder->linkToDeath(this) != OK) {
+    if (binder->remoteBinder() != nullptr &&
+        binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -307,7 +308,9 @@
         return Status::fromExceptionCode(Status::EX_NULL_POINTER);
     }
 
-    if (OK != IInterface::asBinder(callback)->linkToDeath(this)) {
+    if (OK !=
+        IInterface::asBinder(callback)->linkToDeath(
+                sp<ServiceManager>::fromExisting(this))) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -461,7 +464,8 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+    if (OK !=
+        IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
         LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -491,7 +495,7 @@
 }
 
 ssize_t ServiceManager::Service::getNodeStrongRefCount() {
-    sp<BpBinder> bpBinder = binder->remoteBinder();
+    sp<BpBinder> bpBinder = sp<BpBinder>::fromExisting(binder->remoteBinder());
     if (bpBinder == nullptr) return -1;
 
     return ProcessState::self()->getStrongRefCountForNode(bpBinder);
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 627dfe6..8c1beac 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -39,7 +39,7 @@
 class BinderCallback : public LooperCallback {
 public:
     static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
-        sp<BinderCallback> cb = new BinderCallback;
+        sp<BinderCallback> cb = sp<BinderCallback>::make();
 
         int binder_fd = -1;
         IPCThreadState::self()->setupPolling(&binder_fd);
@@ -65,7 +65,7 @@
 class ClientCallbackCallback : public LooperCallback {
 public:
     static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
-        sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+        sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
 
         int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
         LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -105,6 +105,7 @@
         return 1;  // Continue receiving callbacks.
     }
 private:
+    friend sp<ClientCallbackCallback>;
     ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
     sp<ServiceManager> mManager;
 };
@@ -120,7 +121,7 @@
     ps->setThreadPoolMaxThreadCount(0);
     ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
 
-    sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
+    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
     if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
         LOG(ERROR) << "Could not self register servicemanager";
     }
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index fb9f9df..5d5a75e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -46,7 +46,7 @@
         }
     };
 
-    return new LinkableBinder;
+    return sp<LinkableBinder>::make();
 }
 
 class MockAccess : public Access {
@@ -71,7 +71,7 @@
     ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
     ON_CALL(*access, canList(_)).WillByDefault(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
     return sm;
 }
 
@@ -119,7 +119,7 @@
             .uid = uid,
         }));
         EXPECT_CALL(*access, canAdd(_, _)).Times(0);
-        sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+        sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
         EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
             IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -161,7 +161,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -194,7 +194,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -218,7 +218,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     sp<IBinder> service = getBinder();
     EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -244,7 +244,7 @@
     // TODO(b/136023468): when security check is first, this should be called first
     // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -261,7 +261,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     std::vector<std::string> out;
     EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
@@ -329,9 +329,9 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
         Status::EX_SECURITY);
@@ -343,9 +343,9 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     // should always hit security error first
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
@@ -355,7 +355,7 @@
 TEST(ServiceNotifications, InvalidName) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(),
         Status::EX_ILLEGAL_ARGUMENT);
@@ -371,7 +371,7 @@
 TEST(ServiceNotifications, Unregister) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0);
@@ -380,7 +380,7 @@
 TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
         Status::EX_ILLEGAL_STATE);
@@ -389,7 +389,7 @@
 TEST(ServiceNotifications, NoNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
     EXPECT_TRUE(sm->addService("otherservice", getBinder(),
@@ -402,7 +402,7 @@
 TEST(ServiceNotifications, GetNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> service = getBinder();
 
@@ -417,7 +417,7 @@
 TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> service = getBinder();
 
@@ -433,7 +433,7 @@
 TEST(ServiceNotifications, GetMultipleNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> binder1 = getBinder();
     sp<IBinder> binder2 = getBinder();
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 91726cd..c5e3587 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -455,10 +455,10 @@
                                                  __INTRODUCED_IN(29);
 
 /**
- * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
- * frameRate, compatibility, true).
+ * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
+ * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
- * See ASurfaceTransaction_setFrameRateWithSeamlessness().
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
  *
  * Available since API level 30.
  */
@@ -486,19 +486,46 @@
  * influence the system's choice of display frame rate. To specify a compatibility use the
  * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
  *
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
  *
  * Available since API level 31.
  */
-void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
                                       ASurfaceControl* surface_control, float frameRate,
-                                      int8_t compatibility, bool shouldBeSeamless)
+                                      int8_t compatibility, int8_t changeFrameRateStrategy)
                                       __INTRODUCED_IN(31);
 
+/**
+ * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl.
+ *
+ * By default backpressure is disabled, which means submitting a buffer prior to receiving
+ * a callback for the previous buffer could lead to that buffer being "dropped". In cases
+ * where you are selecting for latency, this may be a desirable behavior! We had a new buffer
+ * ready, why shouldn't we show it?
+ *
+ * When back pressure is enabled, each buffer will be required to be presented
+ * before it is released and the callback delivered
+ * (absent the whole SurfaceControl being removed).
+ *
+ * Most apps are likely to have some sort of backpressure internally, e.g. you are
+ * waiting on the callback from frame N-2 before starting frame N. In high refresh
+ * rate scenarios there may not be much time between SurfaceFlinger completing frame
+ * N-1 (and therefore releasing buffer N-2) and beginning frame N. This means
+ * your app may not have enough time to respond in the callback. Using this flag
+ * and pushing buffers earlier for server side queuing will be advantageous
+ * in such cases.
+ *
+ * \param transaction The transaction in which to make the change.
+ * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
+ * \param enableBackPressure Whether to enable back pressure.
+ */
+void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
+                                               ASurfaceControl* surface_control,
+                                               bool enableBackPressure)
+                                               __INTRODUCED_IN(31);
+
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/input/Input.h b/include/input/Input.h
index f9fe6b9..bb5ca0e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -266,6 +266,20 @@
 const char* motionClassificationToString(MotionClassification classification);
 
 /**
+ * Portion of FrameMetrics timeline of interest to input code.
+ */
+enum GraphicsTimeline : size_t {
+    /** Time when the app sent the buffer to SurfaceFlinger. */
+    GPU_COMPLETED_TIME = 0,
+
+    /** Time when the frame was presented on the display */
+    PRESENT_TIME = 1,
+
+    /** Total size of the 'GraphicsTimeline' array. Must always be last. */
+    SIZE = 2
+};
+
+/**
  * Generator of unique numbers used to identify input events.
  *
  * Layout of ID:
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 2deb99d..712adfa 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -177,6 +177,14 @@
     int32_t ordinal;
 };
 
+struct InputDeviceBatteryInfo {
+    explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {}
+    // Name string of the battery.
+    std::string name;
+    // Battery id
+    int32_t id;
+};
+
 /*
  * Describes the characteristics and capabilities of an input device.
  */
@@ -219,6 +227,7 @@
             float min, float max, float flat, float fuzz, float resolution);
     void addMotionRange(const MotionRange& range);
     void addSensorInfo(const InputDeviceSensorInfo& info);
+    void addBatteryInfo(const InputDeviceBatteryInfo& info);
     void addLightInfo(const InputDeviceLightInfo& info);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
@@ -276,6 +285,8 @@
     std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors;
     /* Map from light ID to light info */
     std::unordered_map<int32_t, InputDeviceLightInfo> mLights;
+    /* Map from battery ID to battery info */
+    std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries;
 };
 
 /* Types of input device configuration files. */
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 3e5674e..898d1a9 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -71,6 +71,7 @@
         FOCUS,
         CAPTURE,
         DRAG,
+        TIMELINE,
     };
 
     struct Header {
@@ -195,6 +196,14 @@
 
             inline size_t size() const { return sizeof(Drag); }
         } drag;
+
+        struct Timeline {
+            int32_t eventId;
+            uint32_t empty;
+            std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+            inline size_t size() const { return sizeof(Timeline); }
+        } timeline;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -381,10 +390,25 @@
         nsecs_t consumeTime;
     };
 
-    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
-     * If a signal was received, returns a Finished object.
+    struct Timeline {
+        int32_t inputEventId;
+        std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    };
+
+    typedef std::variant<Finished, Timeline> ConsumerResponse;
+    /* Receive a signal from the consumer in reply to the original dispatch signal.
+     * If a signal was received, returns a Finished or a Timeline object.
+     * The InputConsumer should return a Finished object for every InputMessage that it is sent
+     * to confirm that it has been processed and that the InputConsumer is responsive.
+     * If several InputMessages are sent to InputConsumer, it's possible to receive Finished
+     * events out of order for those messages.
      *
-     * The returned sequence number is never 0 unless the operation failed.
+     * The Timeline object is returned whenever the receiving end has processed a graphical frame
+     * and is returning the timeline of the frame. Not all input events will cause a Timeline
+     * object to be returned, and there is not guarantee about when it will arrive.
+     *
+     * If an object of Finished is returned, the returned sequence number is never 0 unless the
+     * operation failed.
      *
      * Returned error codes:
      *         OK on success.
@@ -392,7 +416,7 @@
      *         DEAD_OBJECT if the channel's peer has been closed.
      *         Other errors probably indicate that the channel is broken.
      */
-    android::base::Result<Finished> receiveFinishedSignal();
+    android::base::Result<ConsumerResponse> receiveConsumerResponse();
 
 private:
     std::shared_ptr<InputChannel> mChannel;
@@ -448,6 +472,9 @@
      */
     status_t sendFinishedSignal(uint32_t seq, bool handled);
 
+    status_t sendTimeline(int32_t inputEventId,
+                          std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
     /* Returns true if there is a deferred event waiting.
      *
      * Should be called after calling consume() to determine whether the consumer
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 144dbd3..cdd8006 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -58,21 +58,17 @@
 // transport itself and should be moved to AIDL or in domain-specific libs.
 //
 // Currently, these are only on system android (not vendor, not host)
+// TODO(b/183654927) - move these into separate libraries
 libbinder_device_interface_sources = [
     "ActivityManager.cpp",
     "AppOpsManager.cpp",
     "IActivityManager.cpp",
     "IAppOpsCallback.cpp",
     "IAppOpsService.cpp",
-    "IBatteryStats.cpp",
-    "IMediaResourceMonitor.cpp",
     "IPermissionController.cpp",
-    "IProcessInfoService.cpp",
     "IUidObserver.cpp",
     "PermissionCache.cpp",
     "PermissionController.cpp",
-    "ProcessInfoService.cpp",
-    "IpPrefix.cpp",
     ":activity_manager_procstate_aidl",
 ]
 
@@ -263,3 +259,48 @@
         },
     },
 }
+
+// libbinder historically contained additional interfaces that provided specific
+// functionality in the platform but have nothing to do with binder itself. These
+// are moved out of libbinder in order to avoid the overhead of their vtables.
+// If you are working on or own one of these interfaces, the responsible things
+// to would be:
+// - give them a new home
+// - convert them to AIDL instead of having manually written parceling code
+
+cc_library {
+    name: "libbatterystats_aidl",
+    srcs: [
+        "IBatteryStats.cpp",
+    ],
+    export_include_dirs: ["include_batterystats"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libprocessinfoservice_aidl",
+    srcs: [
+        "IProcessInfoService.cpp",
+        "ProcessInfoService.cpp",
+    ],
+    export_include_dirs: ["include_processinfo"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "liblog",
+    ],
+}
+
+// TODO(b/183654927): initially empty lib to work around some merge conflicts
+cc_library {
+    name: "libactivitymanager_aidl",
+    srcs: [],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "liblog",
+    ],
+}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 825a821..53b36ff 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -194,10 +194,13 @@
 const String16& BpBinder::getInterfaceDescriptor() const
 {
     if (isDescriptorCached() == false) {
-        Parcel send, reply;
+        sp<BpBinder> thiz = const_cast<BpBinder*>(this);
+
+        Parcel data;
+        data.markForBinder(thiz);
+        Parcel reply;
         // do the IPC without a lock held.
-        status_t err = const_cast<BpBinder*>(this)->transact(
-                INTERFACE_TRANSACTION, send, &reply);
+        status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply);
         if (err == NO_ERROR) {
             String16 res(reply.readString16());
             Mutex::Autolock _l(mLock);
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 1897969..44aa55e 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -63,6 +63,7 @@
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+        reply.readByte();
         return reply.readInt32();
     }
 
@@ -84,6 +85,7 @@
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+        reply.readByte();
         return reply.readInt32();
     }
 
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index d0085df..0de804c 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
 
 #include <utils/Log.h>
 #include <binder/Parcel.h>
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
deleted file mode 100644
index f5fa817..0000000
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2016 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 <binder/IMediaResourceMonitor.h>
-#include <binder/Parcel.h>
-#include <utils/Errors.h>
-#include <sys/types.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
-public:
-    explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
-        : BpInterface<IMediaResourceMonitor>(impl) {}
-
-    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaResourceMonitor::getInterfaceDescriptor());
-        data.writeInt32(pid);
-        data.writeInt32(type);
-        remote()->transact(NOTIFY_RESOURCE_GRANTED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
-
-// ----------------------------------------------------------------------
-
-// NOLINTNEXTLINE(google-default-arguments)
-status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case NOTIFY_RESOURCE_GRANTED: {
-            CHECK_INTERFACE(IMediaResourceMonitor, data, reply);
-            int32_t pid = data.readInt32();
-            const int32_t type = data.readInt32();
-            notifyResourceGranted(/*in*/ pid, /*in*/ type);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------
-
-} // namespace android
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 570edb9..d26754e 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
 #include <binder/Parcel.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
deleted file mode 100644
index 4edc493..0000000
--- a/libs/binder/IpPrefix.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "IpPrefix"
-
-#include <binder/IpPrefix.h>
-#include <vector>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-
-namespace android {
-
-namespace net {
-
-#define RETURN_IF_FAILED(calledOnce)                                     \
-    {                                                                    \
-        status_t returnStatus = calledOnce;                              \
-        if (returnStatus) {                                              \
-            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
-            return returnStatus;                                         \
-         }                                                               \
-    }
-
-status_t IpPrefix::writeToParcel(Parcel* parcel) const {
-    /*
-     * Keep implementation in sync with writeToParcel() in
-     * frameworks/base/core/java/android/net/IpPrefix.java.
-     */
-    std::vector<uint8_t> byte_vector;
-
-    if (mIsIpv6) {
-        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
-        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
-    } else {
-        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
-        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
-    }
-
-    RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
-    RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
-
-    return NO_ERROR;
-}
-
-status_t IpPrefix::readFromParcel(const Parcel* parcel) {
-    /*
-     * Keep implementation in sync with readFromParcel() in
-     * frameworks/base/core/java/android/net/IpPrefix.java.
-     */
-    std::vector<uint8_t> byte_vector;
-
-    RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
-    RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
-
-    if (byte_vector.size() == 16) {
-        mIsIpv6 = true;
-        memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
-
-    } else if (byte_vector.size() == 4) {
-        mIsIpv6 = false;
-        memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
-
-    } else {
-        ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
-        return BAD_VALUE;
-    }
-
-    return NO_ERROR;
-}
-
-const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
-{
-    return mUnion.mIn6Addr;
-}
-
-const struct in_addr& IpPrefix::getAddressAsInAddr() const
-{
-    return mUnion.mInAddr;
-}
-
-bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
-{
-    if (isIpv6()) {
-        *addr = mUnion.mIn6Addr;
-        return true;
-    }
-    return false;
-}
-
-bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
-{
-    if (isIpv4()) {
-        *addr = mUnion.mInAddr;
-        return true;
-    }
-    return false;
-}
-
-bool IpPrefix::isIpv6() const
-{
-    return mIsIpv6;
-}
-
-bool IpPrefix::isIpv4() const
-{
-    return !mIsIpv6;
-}
-
-int32_t IpPrefix::getPrefixLength() const
-{
-    return mPrefixLength;
-}
-
-void IpPrefix::setAddress(const struct in6_addr& addr)
-{
-    mUnion.mIn6Addr = addr;
-    mIsIpv6 = true;
-}
-
-void IpPrefix::setAddress(const struct in_addr& addr)
-{
-    mUnion.mInAddr = addr;
-    mIsIpv6 = false;
-}
-
-void IpPrefix::setPrefixLength(int32_t prefix)
-{
-    mPrefixLength = prefix;
-}
-
-bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
-{
-    if (lhs.mIsIpv6 != rhs.mIsIpv6) {
-        return false;
-    }
-
-    if (lhs.mPrefixLength != rhs.mPrefixLength) {
-        return false;
-    }
-
-    if (lhs.mIsIpv6) {
-        return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
-    }
-
-    return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
-}
-
-}  // namespace net
-
-}  // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7fedba2..4cf4814 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -555,12 +555,17 @@
 }
 
 void Parcel::markForBinder(const sp<IBinder>& binder) {
+    LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
+
     if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
         markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
     }
 }
 
 void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+    LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
+                        "format must be set before data is written OR on IPC data");
+
     LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
     mConnection = connection;
 }
@@ -2100,6 +2105,9 @@
 void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
     const binder_size_t* objects, size_t objectsCount, release_func relFunc)
 {
+    // this code uses 'mOwner == nullptr' to understand whether it owns memory
+    LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
+
     freeData();
 
     mData = const_cast<uint8_t*>(data);
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index f75141e..0fb954a 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/ProcessInfoService.h>
+#include <processinfo/ProcessInfoService.h>
 #include <binder/IServiceManager.h>
 
 #include <utils/Log.h>
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 83a1618..dab3246 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -20,6 +20,7 @@
 
 #include <binder/Parcel.h>
 #include <binder/Stability.h>
+#include <utils/String8.h>
 
 #include "RpcState.h"
 #include "RpcWireFormat.h"
@@ -29,14 +30,20 @@
 #include <sys/un.h>
 #include <unistd.h>
 
-#if defined(__GLIBC__)
+#ifdef __GLIBC__
 extern "C" pid_t gettid();
 #endif
 
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
 namespace android {
 
 using base::unique_fd;
 
+RpcConnection::SocketAddress::~SocketAddress() {}
+
 RpcConnection::RpcConnection() {
     LOG_RPC_DETAIL("RpcConnection created %p", this);
 
@@ -50,65 +57,68 @@
     return new RpcConnection;
 }
 
+class UnixSocketAddress : public RpcConnection::SocketAddress {
+public:
+    explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+        unsigned int pathLen = strlen(path) + 1;
+        LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "%u %s", pathLen, path);
+        memcpy(mAddr.sun_path, path, pathLen);
+    }
+    virtual ~UnixSocketAddress() {}
+    std::string toString() const override {
+        return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+                               mAddr.sun_path)
+                .c_str();
+    }
+    const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+    size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+    sockaddr_un mAddr;
+};
+
 bool RpcConnection::setupUnixDomainServer(const char* path) {
-    LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
-
-    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    if (serverFd == -1) {
-        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    struct sockaddr_un addr = {
-            .sun_family = AF_UNIX,
-    };
-
-    unsigned int pathLen = strlen(path) + 1;
-    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
-    memcpy(addr.sun_path, path, pathLen);
-
-    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
-        ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
-        ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    mServer = std::move(serverFd);
-    return true;
+    return addServer(UnixSocketAddress(path));
 }
 
 bool RpcConnection::addUnixDomainClient(const char* path) {
-    LOG_RPC_DETAIL("Connecting on path: %s", path);
-
-    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    if (serverFd == -1) {
-        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    struct sockaddr_un addr = {
-            .sun_family = AF_UNIX,
-    };
-
-    unsigned int pathLen = strlen(path) + 1;
-    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
-    memcpy(addr.sun_path, path, pathLen);
-
-    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
-        ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
-
-    addClient(std::move(serverFd));
-    return true;
+    return addClient(UnixSocketAddress(path));
 }
 
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcConnection::SocketAddress {
+public:
+    VsockSocketAddress(unsigned int cid, unsigned int port)
+          : mAddr({
+                    .svm_family = AF_VSOCK,
+                    .svm_port = port,
+                    .svm_cid = cid,
+            }) {}
+    virtual ~VsockSocketAddress() {}
+    std::string toString() const override {
+        return String8::format("cid %du port %du", mAddr.svm_cid, mAddr.svm_port).c_str();
+    }
+    const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+    size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+    sockaddr_vm mAddr;
+};
+
+bool RpcConnection::setupVsockServer(unsigned int port) {
+    // realizing value w/ this type at compile time to avoid ubsan abort
+    constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+    return addServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
+    return addClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
 sp<IBinder> RpcConnection::getRootObject() {
     ExclusiveSocket socket(this, SocketUse::CLIENT);
     return state()->getRootObject(socket.fd(), this);
@@ -130,11 +140,8 @@
 void RpcConnection::join() {
     // establish a connection
     {
-        struct sockaddr_un clientSa;
-        socklen_t clientSaLen = sizeof(clientSa);
-
-        unique_fd clientFd(TEMP_FAILURE_RETRY(
-                accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
+        unique_fd clientFd(
+                TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
         if (clientFd < 0) {
             // If this log becomes confusing, should save more state from setupUnixDomainServer
             // in order to output here.
@@ -144,7 +151,7 @@
 
         LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
 
-        addServer(std::move(clientFd));
+        assignServerToThisThread(std::move(clientFd));
     }
 
     // We may not use the connection we just established (two threads might
@@ -170,14 +177,57 @@
     return mForServer;
 }
 
-void RpcConnection::addClient(base::unique_fd&& fd) {
-    std::lock_guard<std::mutex> _l(mSocketMutex);
-    sp<ConnectionSocket> connection = new ConnectionSocket();
-    connection->fd = std::move(fd);
-    mClients.push_back(connection);
+bool RpcConnection::addServer(const SocketAddress& addr) {
+    LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
+
+    unique_fd serverFd(
+            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        ALOGE("Could not create socket: %s", strerror(errno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        int savedErrno = errno;
+        ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+        int savedErrno = errno;
+        ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    mServer = std::move(serverFd);
+    return true;
 }
 
-void RpcConnection::addServer(base::unique_fd&& fd) {
+bool RpcConnection::addClient(const SocketAddress& addr) {
+    unique_fd serverFd(
+            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        int savedErrno = errno;
+        ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        int savedErrno = errno;
+        ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+    std::lock_guard<std::mutex> _l(mSocketMutex);
+    sp<ConnectionSocket> connection = new ConnectionSocket();
+    connection->fd = std::move(serverFd);
+    mClients.push_back(connection);
+    return true;
+}
+
+void RpcConnection::assignServerToThisThread(base::unique_fd&& fd) {
     std::lock_guard<std::mutex> _l(mSocketMutex);
     sp<ConnectionSocket> connection = new ConnectionSocket();
     connection->fd = std::move(fd);
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 64e842e..755ff35 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -192,7 +192,7 @@
         return false;
     }
 
-    ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, 0));
+    ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, MSG_NOSIGNAL));
 
     if (sent < 0 || sent != static_cast<ssize_t>(size)) {
         ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size,
@@ -212,7 +212,7 @@
         return false;
     }
 
-    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL));
+    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL | MSG_NOSIGNAL));
 
     if (recd < 0 || recd != static_cast<ssize_t>(size)) {
         terminate();
@@ -312,8 +312,8 @@
     return waitForReply(fd, connection, reply);
 }
 
-static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize,
-                         const binder_size_t* objects, size_t objectsCount) {
+static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
+                               const binder_size_t* objects, size_t objectsCount) {
     (void)p;
     delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
     (void)dataSize;
@@ -351,7 +351,7 @@
     if (rpcReply->status != OK) return rpcReply->status;
 
     reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
-                               nullptr, 0, cleanup_data);
+                               nullptr, 0, cleanup_reply_data);
 
     reply->markForRpc(connection);
 
@@ -427,6 +427,15 @@
     return processTransactInternal(fd, connection, std::move(transactionData));
 }
 
+static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
+                                        const binder_size_t* objects, size_t objectsCount) {
+    (void)p;
+    (void)data;
+    (void)dataSize;
+    (void)objects;
+    (void)objectsCount;
+}
+
 status_t RpcState::processTransactInternal(const base::unique_fd& fd,
                                            const sp<RpcConnection>& connection,
                                            std::vector<uint8_t>&& transactionData) {
@@ -490,7 +499,12 @@
     }
 
     Parcel data;
-    data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data));
+    // transaction->data is owned by this function. Parcel borrows this data and
+    // only holds onto it for the duration of this function call. Parcel will be
+    // deleted before the 'transactionData' object.
+    data.ipcSetDataReference(transaction->data,
+                             transactionData.size() - offsetof(RpcWireTransaction, data),
+                             nullptr /*object*/, 0 /*objectCount*/, do_nothing_to_transact_data);
     data.markForRpc(connection);
 
     Parcel reply;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index c3f1ba7..f12ef4e 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -38,18 +38,30 @@
     };
 }
 
-void Stability::forceDowngradeCompilationUnit(const sp<IBinder>& binder) {
+void Stability::forceDowngradeToStability(const sp<IBinder>& binder, Level level) {
     // Downgrading a remote binder would require also copying the version from
     // the binder sent here. In practice though, we don't need to downgrade the
     // stability of a remote binder, since this would as an effect only restrict
     // what we can do to it.
     LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
 
-    auto stability = Category::currentFromLevel(getLocalLevel());
+    auto stability = Category::currentFromLevel(level);
     status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
+void Stability::forceDowngradeToLocalStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, getLocalLevel());
+}
+
+void Stability::forceDowngradeToSystemStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, Level::SYSTEM);
+}
+
+void Stability::forceDowngradeToVendorStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, Level::VENDOR);
+}
+
 std::string Stability::Category::debugString() {
     return levelString(level) + " wire protocol version "
         + std::to_string(version);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index eac1bb2..be6667d 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -143,7 +143,8 @@
         OP_COARSE_LOCATION_SOURCE = 109,
         OP_MANAGE_MEDIA = 110,
         OP_BLUETOOTH_CONNECT = 111,
-        _NUM_OP = 112
+        OP_UWB_RANGING = 112,
+        _NUM_OP = 113
     };
 
     AppOpsManager();
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f930d29..b86fc0b 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -186,7 +186,7 @@
 inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
         const String16& _descriptor)
 {
-    if (_descriptor == INTERFACE::descriptor) return this;
+    if (_descriptor == INTERFACE::descriptor) return sp<IInterface>::fromExisting(this);
     return nullptr;
 }
 
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
deleted file mode 100644
index f92d557..0000000
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2016 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
-
-#ifndef __ANDROID_VNDK__
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IMediaResourceMonitor : public IInterface {
-public:
-    DECLARE_META_INTERFACE(MediaResourceMonitor)
-
-    // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
-    enum {
-        TYPE_VIDEO_CODEC = 0,
-        TYPE_AUDIO_CODEC = 1,
-    };
-
-    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
-
-    enum {
-        NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
-public:
-    // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-            uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
deleted file mode 100644
index a8faa3f..0000000
--- a/libs/binder/include/binder/IpPrefix.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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
-
-#ifndef __ANDROID_VNDK__
-
-#include <netinet/in.h>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace net {
-
-/*
- * C++ implementation of the Java class android.net.IpPrefix
- */
-class IpPrefix : public Parcelable {
-public:
-    IpPrefix() = default;
-    virtual ~IpPrefix() = default;
-    IpPrefix(const IpPrefix& prefix) = default;
-
-    IpPrefix(const struct in6_addr& addr, int32_t plen):
-        mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
-
-    IpPrefix(const struct in_addr& addr, int32_t plen):
-        mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
-
-    bool getAddressAsIn6Addr(struct in6_addr* addr) const;
-    bool getAddressAsInAddr(struct in_addr* addr) const;
-
-    const struct in6_addr& getAddressAsIn6Addr() const;
-    const struct in_addr& getAddressAsInAddr() const;
-
-    bool isIpv6() const;
-    bool isIpv4() const;
-
-    int32_t getPrefixLength() const;
-
-    void setAddress(const struct in6_addr& addr);
-    void setAddress(const struct in_addr& addr);
-
-    void setPrefixLength(int32_t prefix);
-
-    friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
-
-    friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
-        return !(lhs == rhs);
-    }
-
-public:
-    // Overrides
-    status_t writeToParcel(Parcel* parcel) const override;
-    status_t readFromParcel(const Parcel* parcel) override;
-
-private:
-    union InternalUnion {
-        InternalUnion() = default;
-        explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
-        explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
-        struct in6_addr mIn6Addr;
-        struct in_addr mInAddr;
-    } mUnion;
-    int32_t mPrefixLength;
-    bool mIsIpv6;
-};
-
-}  // namespace net
-
-}  // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9578372..211790d 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -101,10 +101,6 @@
     // is for an RPC transaction).
     void markForBinder(const sp<IBinder>& binder);
 
-    // Whenever possible, markForBinder should be preferred. This method is
-    // called automatically on reply Parcels for RPC transactions.
-    void markForRpc(const sp<RpcConnection>& connection);
-
     // Whether this Parcel is written for RPC transactions (after calls to
     // markForBinder or markForRpc).
     bool isForRpc() const;
@@ -540,6 +536,10 @@
                                             const binder_size_t* objects, size_t objectsCount,
                                             release_func relFunc);
 
+    // Whenever possible, markForBinder should be preferred. This method is
+    // called automatically on reply Parcels for RPC transactions.
+    void markForRpc(const sp<RpcConnection>& connection);
+
     status_t            finishWrite(size_t len);
     void                releaseObjects();
     void                acquireObjects();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 2405ab6..ca29440 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -124,7 +124,6 @@
 
             Vector<handle_entry>mHandleToObject;
 
-            String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
 
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index 65c5232..efa922d 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -61,6 +61,18 @@
      */
     [[nodiscard]] bool addUnixDomainClient(const char* path);
 
+#ifdef __BIONIC__
+    /**
+     * Creates an RPC server at the current port.
+     */
+    [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+    /**
+     * Connects to an RPC server at the CVD & port.
+     */
+    [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+#endif // __BIONIC__
+
     /**
      * Query the other side of the connection for the root object hosted by that
      * process's RpcServer (if one exists)
@@ -85,11 +97,21 @@
     // internal only
     const std::unique_ptr<RpcState>& state() { return mState; }
 
+    class SocketAddress {
+    public:
+        virtual ~SocketAddress();
+        virtual std::string toString() const = 0;
+        virtual const sockaddr* addr() const = 0;
+        virtual size_t addrSize() const = 0;
+    };
+
 private:
+    friend sp<RpcConnection>;
     RpcConnection();
 
-    void addServer(base::unique_fd&& fd);
-    void addClient(base::unique_fd&& fd);
+    bool addServer(const SocketAddress& address);
+    bool addClient(const SocketAddress& address);
+    void assignServerToThisThread(base::unique_fd&& fd);
 
     struct ConnectionSocket : public RefBase {
         base::unique_fd fd;
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index a09e587..f4bfac8 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -54,12 +54,37 @@
     // Given a binder interface at a certain stability, there may be some
     // requirements associated with that higher stability level. For instance, a
     // VINTF stability binder is required to be in the VINTF manifest. This API
-    // can be called to use that same interface within a partition.
-    static void forceDowngradeCompilationUnit(const sp<IBinder>& binder);
+    // can be called to use that same interface within the local partition.
+    static void forceDowngradeToLocalStability(const sp<IBinder>& binder);
 
     // WARNING: Below APIs are only ever expected to be called by auto-generated code.
     //     Instead of calling them, you should set the stability of a .aidl interface
 
+    // WARNING: The only client of
+    //      - forceDowngradeToSystemStability() and;
+    //      - korceDowngradeToVendorStability()
+    //  should be AIBinder_forceDowngradeToLocalStability().
+    //
+    // getLocalLevel() in libbinder returns Level::SYSTEM when called
+    // from libbinder_ndk (even on vendor partition). So we explicitly provide
+    // these methods for use by the NDK API:
+    //      AIBinder_forceDowngradeToLocalStability().
+    //
+    // This allows correctly downgrading the binder's stability to either system/vendor,
+    // depending on the partition.
+
+    // Given a binder interface at a certain stability, there may be some
+    // requirements associated with that higher stability level. For instance, a
+    // VINTF stability binder is required to be in the VINTF manifest. This API
+    // can be called to use that same interface within the vendor partition.
+    static void forceDowngradeToVendorStability(const sp<IBinder>& binder);
+
+    // Given a binder interface at a certain stability, there may be some
+    // requirements associated with that higher stability level. For instance, a
+    // VINTF stability binder is required to be in the VINTF manifest. This API
+    // can be called to use that same interface within the system partition.
+    static void forceDowngradeToSystemStability(const sp<IBinder>& binder);
+
     // WARNING: This is only ever expected to be called by auto-generated code. You likely want to
     // change or modify the stability class of the interface you are using.
     // This must be called as soon as the binder in question is constructed. No thread safety
@@ -146,6 +171,9 @@
     // returns the stability according to how this was built
     static Level getLocalLevel();
 
+    // Downgrades binder stability to the specified level.
+    static void forceDowngradeToStability(const sp<IBinder>& binder, Level level);
+
     enum {
       REPR_NONE = 0,
       REPR_LOG = 1,
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include_batterystats/batterystats/IBatteryStats.h
similarity index 100%
rename from libs/binder/include/binder/IBatteryStats.h
rename to libs/binder/include_batterystats/batterystats/IBatteryStats.h
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include_processinfo/processinfo/IProcessInfoService.h
similarity index 100%
rename from libs/binder/include/binder/IProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/IProcessInfoService.h
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
similarity index 98%
rename from libs/binder/include/binder/ProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/ProcessInfoService.h
index 6b3b5ce..978856d 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
@@ -18,7 +18,7 @@
 
 #ifndef __ANDROID_VNDK__
 
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 #include <sys/types.h>
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 3c90681..3e6c39f 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -598,6 +598,8 @@
     }
 
     *in = new AParcel(binder);
+    (*in)->get()->markForBinder(binder->getBinder());
+
     status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
     binder_status_t ret = PruneStatusT(status);
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 8941e49..b9adc9a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,6 +36,9 @@
 
 __BEGIN_DECLS
 
+/**
+ * Flags for AIBinder_transact.
+ */
 typedef uint32_t binder_flags_t;
 enum {
     /**
@@ -47,7 +50,10 @@
     FLAG_ONEWAY = 0x01,
 };
 
-// Also see IBinder.h in libbinder
+/**
+ * Codes for AIBinder_transact. This defines the range of codes available for
+ * usage. Other codes are used or reserved by the Android system.
+ */
 typedef uint32_t transaction_code_t;
 enum {
     /**
@@ -202,7 +208,8 @@
  *
  * Available since API level 29.
  *
- * \param dump function to call when an instance of this binder class is being dumped.
+ * \param clazz class which should use this dump function
+ * \param onDump function to call when an instance of this binder class is being dumped.
  */
 void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index b4dc08a..6f1fdfc 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -189,7 +189,7 @@
  *
  * Available since API level 29.
  *
- * \param a low-level error to associate with this status object.
+ * \param status a low-level error to associate with this status object.
  *
  * \return a newly constructed status object that the caller owns.
  */
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index ce7255e..f113ba8 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -45,6 +45,18 @@
     AIBinder_markVendorStability(binder);
 }
 
+/**
+ * Given a binder interface at a certain stability, there may be some
+ * requirements associated with that higher stability level. For instance, a
+ * VINTF stability binder is required to be in the VINTF manifest. This API
+ * can be called to use that same interface within the vendor partition.
+ */
+void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+    AIBinder_forceDowngradeToVendorStability(binder);
+}
+
 #else  // defined(__ANDROID_VENDOR__)
 
 enum {
@@ -62,9 +74,27 @@
     AIBinder_markSystemStability(binder);
 }
 
+/**
+ * Given a binder interface at a certain stability, there may be some
+ * requirements associated with that higher stability level. For instance, a
+ * VINTF stability binder is required to be in the VINTF manifest. This API
+ * can be called to use that same interface within the system partition.
+ */
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+    AIBinder_forceDowngradeToSystemStability(binder);
+}
+
 #endif  // defined(__ANDROID_VENDOR__)
 
 /**
+ * WARNING: this is not expected to be used manually. When the build system has
+ * versioned checks in place for an interface that prevent it being changed year
+ * over year (specifically like those for @VintfStability stable AIDL
+ * interfaces), this could be called. Calling this without this or equivalent
+ * infrastructure will lead to de facto frozen APIs or GSI test failures.
+ *
  * This interface has system<->vendor stability
  */
 void AIBinder_markVintfStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index f1db653..67c85b6 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -127,6 +127,9 @@
     AServiceManager_tryUnregister; # llndk
     AServiceManager_reRegister; # llndk
 
+    AIBinder_forceDowngradeToSystemStability; # apex
+    AIBinder_forceDowngradeToVendorStability; # llndk
+
     AIBinder_Class_getDescriptor;
     AIBinder_Weak_clone;
     AIBinder_Weak_lt;
diff --git a/libs/binder/ndk/parcel_internal.h b/libs/binder/ndk/parcel_internal.h
index 6b7295e..b4f6358 100644
--- a/libs/binder/ndk/parcel_internal.h
+++ b/libs/binder/ndk/parcel_internal.h
@@ -27,9 +27,8 @@
     const ::android::Parcel* get() const { return mParcel; }
     ::android::Parcel* get() { return mParcel; }
 
-    explicit AParcel(const AIBinder* binder)
-        : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
-    AParcel(const AIBinder* binder, ::android::Parcel* parcel, bool owns)
+    explicit AParcel(AIBinder* binder) : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
+    AParcel(AIBinder* binder, ::android::Parcel* parcel, bool owns)
         : mBinder(binder), mParcel(parcel), mOwns(owns) {}
 
     ~AParcel() {
@@ -38,7 +37,7 @@
         }
     }
 
-    static const AParcel readOnly(const AIBinder* binder, const ::android::Parcel* parcel) {
+    static const AParcel readOnly(AIBinder* binder, const ::android::Parcel* parcel) {
         return AParcel(binder, const_cast<::android::Parcel*>(parcel), false);
     }
 
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index a5b3ece..7eafb9c 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -31,7 +31,7 @@
 #error libbinder_ndk should only be built in a system context
 #endif
 
-// explicit extern because symbol is only declared in header when __ANDROID_VNDK__
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
 extern "C" void AIBinder_markVendorStability(AIBinder* binder) {
     Stability::markVndk(binder->getBinder().get());
 }
@@ -43,3 +43,12 @@
 void AIBinder_markVintfStability(AIBinder* binder) {
     Stability::markVintf(binder->getBinder().get());
 }
+
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
+extern "C" void AIBinder_forceDowngradeToVendorStability(AIBinder* binder) {
+    Stability::forceDowngradeToVendorStability(binder->getBinder());
+}
+
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder) {
+    Stability::forceDowngradeToSystemStability(binder->getBinder());
+}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a44cddf..afc4b1b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -104,22 +104,49 @@
     require_root: true,
 }
 
-cc_test {
-    name: "binderRpcTest",
-    defaults: ["binder_test_defaults"],
-
+aidl_interface {
+    name: "binderRpcTestIface",
+    host_supported: true,
+    unstable: true,
     srcs: [
         "IBinderRpcSession.aidl",
         "IBinderRpcTest.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test {
+    name: "binderRpcTest",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    defaults: [
+        "binder_test_defaults",
+        "libbinder_ndk_host_user",
+    ],
+
+    srcs: [
         "binderRpcTest.cpp",
     ],
     shared_libs: [
         "libbinder",
+        "libbinder_ndk",
         "libbase",
         "libutils",
         "libcutils",
         "liblog",
     ],
+    static_libs: [
+        "binderRpcTestIface-cpp",
+        "binderRpcTestIface-ndk_platform",
+    ],
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -210,6 +237,11 @@
     srcs: [
         "IBinderStabilityTest.aidl",
     ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+    },
 }
 
 cc_test {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 6fa5333..985d086 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-#include <sys/prctl.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <cstdlib>
-#include <iostream>
-#include <thread>
-
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
+#include <aidl/IBinderRpcTest.h>
 #include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
 #include <binder/IServiceManager.h>
@@ -33,10 +28,29 @@
 #include <binder/RpcServer.h>
 #include <gtest/gtest.h>
 
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif //__BIONIC__
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
 #include "../RpcState.h" // for debugging
 
 namespace android {
 
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+    Parcel p;
+    p.writeInt32(3);
+
+    EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \
@@ -185,8 +199,13 @@
 
 static std::string allocateSocketAddress() {
     static size_t id = 0;
+    static bool gUseTmp = access("/tmp/", F_OK) != -1;
 
-    return "/dev/binderRpcTest_" + std::to_string(id++);
+    if (gUseTmp) {
+        return "/tmp/binderRpcTest_" + std::to_string(id++);
+    } else {
+        return "/dev/binderRpcTest_" + std::to_string(id++);
+    }
 };
 
 struct ProcessConnection {
@@ -214,58 +233,6 @@
     }
 };
 
-// This creates a new process serving an interface on a certain number of
-// threads.
-ProcessConnection createRpcTestSocketServerProcess(
-        size_t numThreads,
-        const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
-    CHECK_GT(numThreads, 0);
-
-    std::string addr = allocateSocketAddress();
-    unlink(addr.c_str());
-
-    auto ret = ProcessConnection{
-            .host = Process([&] {
-                sp<RpcServer> server = RpcServer::make();
-
-                server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
-                // server supporting one client on one socket
-                sp<RpcConnection> connection = server->addClientConnection();
-                CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
-
-                configure(server, connection);
-
-                // accept 'numThreads' connections
-                std::vector<std::thread> pool;
-                for (size_t i = 0; i + 1 < numThreads; i++) {
-                    pool.push_back(std::thread([=] { connection->join(); }));
-                }
-                connection->join();
-                for (auto& t : pool) t.join();
-            }),
-            .connection = RpcConnection::make(),
-    };
-
-    // wait up to 1s for sockets to be created
-    constexpr useconds_t kMaxWaitUs = 1000000;
-    constexpr useconds_t kWaitDivision = 100;
-    for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) {
-        usleep(kMaxWaitUs / kWaitDivision);
-    }
-
-    // create remainder of connections
-    for (size_t i = 0; i < numThreads; i++) {
-        // Connection refused sometimes after file created but before listening.
-        CHECK(ret.connection->addUnixDomainClient(addr.c_str()) ||
-              (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str())))
-                << i;
-    }
-
-    ret.rootBinder = ret.connection->getRootObject();
-    return ret;
-}
-
 // Process connection where the process hosts IBinderRpcTest, the server used
 // for most testing here
 struct BinderRpcTestProcessConnection {
@@ -290,26 +257,122 @@
     }
 };
 
-BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
-    BinderRpcTestProcessConnection ret{
-            .proc = createRpcTestSocketServerProcess(numThreads,
-                                                     [&](const sp<RpcServer>& server,
-                                                         const sp<RpcConnection>& connection) {
-                                                         sp<MyBinderRpcTest> service =
-                                                                 new MyBinderRpcTest;
-                                                         server->setRootObject(service);
-                                                         service->connection =
-                                                                 connection; // for testing only
-                                                     }),
-    };
-
-    ret.rootBinder = ret.proc.rootBinder;
-    ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
-
-    return ret;
+enum class SocketType {
+    UNIX,
+#ifdef __BIONIC__
+    VSOCK,
+#endif // __BIONIC__
+};
+static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
+    switch (info.param) {
+        case SocketType::UNIX:
+            return "unix_domain_socket";
+#ifdef __BIONIC__
+        case SocketType::VSOCK:
+            return "vm_socket";
+#endif // __BIONIC__
+        default:
+            LOG_ALWAYS_FATAL("Unknown socket type");
+            return "";
+    }
 }
+class BinderRpc : public ::testing::TestWithParam<SocketType> {
+public:
+    // This creates a new process serving an interface on a certain number of
+    // threads.
+    ProcessConnection createRpcTestSocketServerProcess(
+            size_t numThreads,
+            const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
+        CHECK_GT(numThreads, 0);
 
-TEST(BinderRpc, RootObjectIsNull) {
+        SocketType socketType = GetParam();
+
+        std::string addr = allocateSocketAddress();
+        unlink(addr.c_str());
+        static unsigned int port = 3456;
+        port++;
+
+        auto ret = ProcessConnection{
+                .host = Process([&] {
+                    sp<RpcServer> server = RpcServer::make();
+
+                    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+                    // server supporting one client on one socket
+                    sp<RpcConnection> connection = server->addClientConnection();
+
+                    switch (socketType) {
+                        case SocketType::UNIX:
+                            CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+                            break;
+#ifdef __BIONIC__
+                        case SocketType::VSOCK:
+                            CHECK(connection->setupVsockServer(port));
+                            break;
+#endif // __BIONIC__
+                        default:
+                            LOG_ALWAYS_FATAL("Unknown socket type");
+                    }
+
+                    configure(server, connection);
+
+                    // accept 'numThreads' connections
+                    std::vector<std::thread> pool;
+                    for (size_t i = 0; i + 1 < numThreads; i++) {
+                        pool.push_back(std::thread([=] { connection->join(); }));
+                    }
+                    connection->join();
+                    for (auto& t : pool) t.join();
+                }),
+                .connection = RpcConnection::make(),
+        };
+
+        // create remainder of connections
+        for (size_t i = 0; i < numThreads; i++) {
+            for (size_t tries = 0; tries < 5; tries++) {
+                usleep(10000);
+                switch (socketType) {
+                    case SocketType::UNIX:
+                        if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
+                        break;
+#ifdef __BIONIC__
+                    case SocketType::VSOCK:
+                        if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
+                        break;
+#endif // __BIONIC__
+                    default:
+                        LOG_ALWAYS_FATAL("Unknown socket type");
+                }
+            }
+            LOG_ALWAYS_FATAL("Could not connect");
+        success:;
+        }
+
+        ret.rootBinder = ret.connection->getRootObject();
+        return ret;
+    }
+
+    BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+        BinderRpcTestProcessConnection ret{
+                .proc = createRpcTestSocketServerProcess(numThreads,
+                                                         [&](const sp<RpcServer>& server,
+                                                             const sp<RpcConnection>& connection) {
+                                                             sp<MyBinderRpcTest> service =
+                                                                     new MyBinderRpcTest;
+                                                             server->setRootObject(service);
+                                                             service->connection =
+                                                                     connection; // for testing only
+                                                         }),
+        };
+
+        ret.rootBinder = ret.proc.rootBinder;
+        ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+        return ret;
+    }
+};
+
+TEST_P(BinderRpc, RootObjectIsNull) {
     auto proc = createRpcTestSocketServerProcess(1,
                                                  [](const sp<RpcServer>& server,
                                                     const sp<RpcConnection>&) {
@@ -324,20 +387,26 @@
     EXPECT_EQ(nullptr, proc.connection->getRootObject());
 }
 
-TEST(BinderRpc, Ping) {
+TEST_P(BinderRpc, Ping) {
     auto proc = createRpcTestSocketServerProcess(1);
     ASSERT_NE(proc.rootBinder, nullptr);
     EXPECT_EQ(OK, proc.rootBinder->pingBinder());
 }
 
-TEST(BinderRpc, TransactionsMustBeMarkedRpc) {
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    ASSERT_NE(proc.rootBinder, nullptr);
+    EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
     auto proc = createRpcTestSocketServerProcess(1);
     Parcel data;
     Parcel reply;
     EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
 }
 
-TEST(BinderRpc, UnknownTransaction) {
+TEST_P(BinderRpc, UnknownTransaction) {
     auto proc = createRpcTestSocketServerProcess(1);
     Parcel data;
     data.markForBinder(proc.rootBinder);
@@ -345,19 +414,19 @@
     EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
 }
 
-TEST(BinderRpc, SendSomethingOneway) {
+TEST_P(BinderRpc, SendSomethingOneway) {
     auto proc = createRpcTestSocketServerProcess(1);
     EXPECT_OK(proc.rootIface->sendString("asdf"));
 }
 
-TEST(BinderRpc, SendAndGetResultBack) {
+TEST_P(BinderRpc, SendAndGetResultBack) {
     auto proc = createRpcTestSocketServerProcess(1);
     std::string doubled;
     EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
     EXPECT_EQ("cool cool ", doubled);
 }
 
-TEST(BinderRpc, SendAndGetResultBackBig) {
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
     auto proc = createRpcTestSocketServerProcess(1);
     std::string single = std::string(1024, 'a');
     std::string doubled;
@@ -365,7 +434,7 @@
     EXPECT_EQ(single + single, doubled);
 }
 
-TEST(BinderRpc, CallMeBack) {
+TEST_P(BinderRpc, CallMeBack) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     int32_t pingResult;
@@ -375,7 +444,7 @@
     EXPECT_EQ(0, MyBinderRpcSession::gNum);
 }
 
-TEST(BinderRpc, RepeatBinder) {
+TEST_P(BinderRpc, RepeatBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> inBinder = new MyBinderRpcSession("foo");
@@ -397,7 +466,7 @@
     EXPECT_EQ(0, MyBinderRpcSession::gNum);
 }
 
-TEST(BinderRpc, RepeatTheirBinder) {
+TEST_P(BinderRpc, RepeatTheirBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinderRpcSession> session;
@@ -421,7 +490,7 @@
     EXPECT_EQ(nullptr, weak.promote());
 }
 
-TEST(BinderRpc, RepeatBinderNull) {
+TEST_P(BinderRpc, RepeatBinderNull) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> outBinder;
@@ -429,7 +498,7 @@
     EXPECT_EQ(nullptr, outBinder);
 }
 
-TEST(BinderRpc, HoldBinder) {
+TEST_P(BinderRpc, HoldBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     IBinder* ptr = nullptr;
@@ -455,7 +524,7 @@
 // These are behavioral differences form regular binder, where certain usecases
 // aren't supported.
 
-TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
     auto proc1 = createRpcTestSocketServerProcess(1);
     auto proc2 = createRpcTestSocketServerProcess(1);
 
@@ -464,7 +533,7 @@
               proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
 }
 
-TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
@@ -473,7 +542,7 @@
               proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
 }
 
-TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     // for historical reasons, IServiceManager interface only returns the
@@ -484,7 +553,7 @@
 
 // END TESTS FOR LIMITATIONS OF SOCKET BINDER
 
-TEST(BinderRpc, RepeatRootObject) {
+TEST_P(BinderRpc, RepeatRootObject) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> outBinder;
@@ -492,7 +561,7 @@
     EXPECT_EQ(proc.rootBinder, outBinder);
 }
 
-TEST(BinderRpc, NestedTransactions) {
+TEST_P(BinderRpc, NestedTransactions) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     auto nastyNester = sp<MyBinderRpcTest>::make();
@@ -503,7 +572,7 @@
     EXPECT_EQ(nullptr, weak.promote());
 }
 
-TEST(BinderRpc, SameBinderEquality) {
+TEST_P(BinderRpc, SameBinderEquality) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> a;
@@ -515,7 +584,7 @@
     EXPECT_EQ(a, b);
 }
 
-TEST(BinderRpc, SameBinderEqualityWeak) {
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> a;
@@ -547,7 +616,7 @@
         EXPECT_EQ(expected, session);                     \
     } while (false)
 
-TEST(BinderRpc, SingleSession) {
+TEST_P(BinderRpc, SingleSession) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinderRpcSession> session;
@@ -561,7 +630,7 @@
     expectSessions(0, proc.rootIface);
 }
 
-TEST(BinderRpc, ManySessions) {
+TEST_P(BinderRpc, ManySessions) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     std::vector<sp<IBinderRpcSession>> sessions;
@@ -595,7 +664,7 @@
     return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
 }
 
-TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
     constexpr size_t kNumThreads = 10;
 
     auto proc = createRpcTestSocketServerProcess(kNumThreads);
@@ -627,7 +696,7 @@
     for (auto& t : ts) t.join();
 }
 
-TEST(BinderRpc, ThreadPoolOverSaturated) {
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     constexpr size_t kSleepMs = 500;
@@ -651,7 +720,7 @@
     EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
 }
 
-TEST(BinderRpc, ThreadingStressTest) {
+TEST_P(BinderRpc, ThreadingStressTest) {
     constexpr size_t kNumClientThreads = 10;
     constexpr size_t kNumServerThreads = 10;
     constexpr size_t kNumCalls = 100;
@@ -672,7 +741,7 @@
     for (auto& t : threads) t.join();
 }
 
-TEST(BinderRpc, OnewayCallDoesNotWait) {
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
     constexpr size_t kReallyLongTimeMs = 100;
     constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
 
@@ -687,7 +756,7 @@
     EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
 }
 
-TEST(BinderRpc, OnewayCallQueueing) {
+TEST_P(BinderRpc, OnewayCallQueueing) {
     constexpr size_t kNumSleeps = 10;
     constexpr size_t kNumExtraServerThreads = 4;
     constexpr size_t kSleepMs = 50;
@@ -711,10 +780,7 @@
     EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
 }
 
-TEST(BinderRpc, Die) {
-    // TODO(b/183141167): handle this in library
-    signal(SIGPIPE, SIG_IGN);
-
+TEST_P(BinderRpc, Die) {
     for (bool doDeathCleanup : {true, false}) {
         auto proc = createRpcTestSocketServerProcess(1);
 
@@ -733,6 +799,30 @@
     }
 }
 
+TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+    ASSERT_NE(binder, nullptr);
+
+    ASSERT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+    ASSERT_NE(binder, nullptr);
+
+    auto ndkBinder = aidl::IBinderRpcTest::fromBinder(binder);
+    ASSERT_NE(ndkBinder, nullptr);
+
+    std::string out;
+    ndk::ScopedAStatus status = ndkBinder->doubleString("aoeu", &out);
+    ASSERT_TRUE(status.isOk()) << status.getDescription();
+    ASSERT_EQ("aoeuaoeu", out);
+}
+
 ssize_t countFds() {
     DIR* dir = opendir("/proc/self/fd/");
     if (dir == nullptr) return -1;
@@ -743,7 +833,7 @@
     return ret;
 }
 
-TEST(BinderRpc, Fds) {
+TEST_P(BinderRpc, Fds) {
     ssize_t beforeFds = countFds();
     ASSERT_GE(beforeFds, 0);
     {
@@ -753,10 +843,19 @@
     ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
 }
 
-extern "C" int main(int argc, char** argv) {
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+                        ::testing::Values(SocketType::UNIX
+#ifdef __BIONIC__
+                                          ,
+                                          SocketType::VSOCK
+#endif // __BIONIC__
+                                          ),
+                        PrintSocketType);
+
+} // namespace android
+
+int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
     return RUN_ALL_TESTS();
 }
-
-} // namespace android
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 4270540..cb309bd 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/binder_libbinder.h>
 #include <android/binder_manager.h>
 #include <android/binder_stability.h>
 #include <binder/Binder.h>
@@ -131,17 +132,55 @@
     EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
 }
 
-TEST(BinderStability, ForceDowngradeStability) {
+TEST(BinderStability, ForceDowngradeToLocalStability) {
     sp<IBinder> someBinder = BadStableBinder::vintf();
 
     EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
 
     // silly to do this after already using the binder, but it's for the test
-    Stability::forceDowngradeCompilationUnit(someBinder);
+    Stability::forceDowngradeToLocalStability(someBinder);
 
     EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
 }
 
+TEST(BinderStability, NdkForceDowngradeToLocalStability) {
+    sp<IBinder> someBinder = BadStableBinder::vintf();
+
+    EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
+
+    // silly to do this after already using the binder, but it's for the test
+    AIBinder_forceDowngradeToLocalStability(AIBinder_fromPlatformBinder(someBinder));
+
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
+TEST(BinderStability, ForceDowngradeToVendorStability) {
+    sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+    auto server = interface_cast<IBinderStabilityTest>(serverBinder);
+
+    ASSERT_NE(nullptr, server.get());
+    ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder());
+
+    {
+        sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+        EXPECT_TRUE(Stability::requiresVintfDeclaration(binder));
+        EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+        EXPECT_TRUE(binder->gotUserTransaction);
+    }
+    {
+        sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+        // This method should never be called directly. This is done only for the test.
+        Stability::forceDowngradeToVendorStability(binder);
+
+        // Binder downgraded to vendor stability, cannot be called from system context
+        EXPECT_FALSE(Stability::requiresVintfDeclaration(binder));
+        EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode());
+        EXPECT_FALSE(binder->gotUserTransaction);
+    }
+}
+
 TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
     sp<IBinder> vintfServer = BadStableBinder::vintf();
 
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1d7ed2f..e5afd40 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -570,11 +570,13 @@
         }).detach();
     }
 
-    status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
-        if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+    status_t setFrameRate(float frameRate, int8_t compatibility,
+                          int8_t changeFrameRateStrategy) override {
+        if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                               "BBQSurface::setFrameRate")) {
             return BAD_VALUE;
         }
-        return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
+        return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
     }
 
     status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 97f8f47..454aa9e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -982,6 +982,34 @@
         return NO_ERROR;
     }
 
+    status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                     const sp<gui::IHdrLayerInfoListener>& listener) override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, displayToken);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER, data, &reply);
+        if (error != OK) {
+            ALOGE("addHdrLayerInfoListener: Failed to transact; error = %d", error);
+        }
+        return error;
+    }
+
+    status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                        const sp<gui::IHdrLayerInfoListener>& listener) override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, displayToken);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER, data, &reply);
+        if (error != OK) {
+            ALOGE("removeHdrLayerInfoListener: Failed to transact; error = %d", error);
+        }
+        return error;
+    }
+
     status_t notifyPowerBoost(int32_t boostId) override {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1033,39 +1061,15 @@
     }
 
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                          int8_t compatibility, bool shouldBeSeamless) override {
+                          int8_t compatibility, int8_t changeFrameRateStrategy) override {
         Parcel data, reply;
-        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
-            return err;
-        }
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface));
+        SAFE_PARCEL(data.writeFloat, frameRate);
+        SAFE_PARCEL(data.writeByte, compatibility);
+        SAFE_PARCEL(data.writeByte, changeFrameRateStrategy);
 
-        err = data.writeStrongBinder(IInterface::asBinder(surface));
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeFloat(frameRate);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeByte(compatibility);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeBool(shouldBeSeamless);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+        status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
         if (err != NO_ERROR) {
             ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
             return err;
@@ -1862,6 +1866,38 @@
             }
             return setDisplayBrightness(displayToken, brightness);
         }
+        case ADD_HDR_LAYER_INFO_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> displayToken;
+            status_t error = data.readNullableStrongBinder(&displayToken);
+            if (error != NO_ERROR) {
+                ALOGE("addHdrLayerInfoListener: Failed to read display token");
+                return error;
+            }
+            sp<gui::IHdrLayerInfoListener> listener;
+            error = data.readNullableStrongBinder(&listener);
+            if (error != NO_ERROR) {
+                ALOGE("addHdrLayerInfoListener: Failed to read listener");
+                return error;
+            }
+            return addHdrLayerInfoListener(displayToken, listener);
+        }
+        case REMOVE_HDR_LAYER_INFO_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> displayToken;
+            status_t error = data.readNullableStrongBinder(&displayToken);
+            if (error != NO_ERROR) {
+                ALOGE("removeHdrLayerInfoListener: Failed to read display token");
+                return error;
+            }
+            sp<gui::IHdrLayerInfoListener> listener;
+            error = data.readNullableStrongBinder(&listener);
+            if (error != NO_ERROR) {
+                ALOGE("removeHdrLayerInfoListener: Failed to read listener");
+                return error;
+            }
+            return removeHdrLayerInfoListener(displayToken, listener);
+        }
         case NOTIFY_POWER_BOOST: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             int32_t boostId;
@@ -1894,36 +1930,24 @@
         case SET_FRAME_RATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> binder;
-            status_t err = data.readStrongBinder(&binder);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
-                return err;
-            }
+            SAFE_PARCEL(data.readStrongBinder, &binder);
+
             sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
             if (!surface) {
-                ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
-                      strerror(-err), -err);
-                return err;
+                ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer");
+                return BAD_VALUE;
             }
             float frameRate;
-            err = data.readFloat(&frameRate);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
-                return err;
-            }
+            SAFE_PARCEL(data.readFloat, &frameRate);
+
             int8_t compatibility;
-            err = data.readByte(&compatibility);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
-                return err;
-            }
-            bool shouldBeSeamless;
-            err = data.readBool(&shouldBeSeamless);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
-                return err;
-            }
-            status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
+            SAFE_PARCEL(data.readByte, &compatibility);
+
+            int8_t changeFrameRateStrategy;
+            SAFE_PARCEL(data.readByte, &changeFrameRateStrategy);
+
+            status_t result =
+                    setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
             reply->writeInt32(result);
             return NO_ERROR;
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 8c21553..0ca4977 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -19,6 +19,7 @@
 #include <apex/window.h>
 #include <inttypes.h>
 
+#include <android/native_window.h>
 #include <binder/Parcel.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposerClient.h>
@@ -60,7 +61,7 @@
         frameRateSelectionPriority(-1),
         frameRate(0.0f),
         frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
-        shouldBeSeamless(true),
+        changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         autoRefresh(false),
@@ -148,7 +149,7 @@
     SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
     SAFE_PARCEL(output.writeFloat, frameRate);
     SAFE_PARCEL(output.writeByte, frameRateCompatibility);
-    SAFE_PARCEL(output.writeBool, shouldBeSeamless);
+    SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(output.writeBool, autoRefresh);
@@ -269,7 +270,7 @@
     SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
     SAFE_PARCEL(input.readFloat, &frameRate);
     SAFE_PARCEL(input.readByte, &frameRateCompatibility);
-    SAFE_PARCEL(input.readBool, &shouldBeSeamless);
+    SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
     SAFE_PARCEL(input.readUint32, &tmpUint32);
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readUint64, &frameNumber);
@@ -527,7 +528,7 @@
         what |= eFrameRateChanged;
         frameRate = other.frameRate;
         frameRateCompatibility = other.frameRateCompatibility;
-        shouldBeSeamless = other.shouldBeSeamless;
+        changeFrameRateStrategy = other.changeFrameRateStrategy;
     }
     if (other.what & eFixedTransformHintChanged) {
         what |= eFixedTransformHintChanged;
@@ -559,6 +560,10 @@
     }
 }
 
+bool layer_state_t::hasBufferChanges() const {
+    return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+}
+
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
     SAFE_PARCEL(output.writeFloat, dsdx);
     SAFE_PARCEL(output.writeFloat, dtdx);
@@ -620,8 +625,8 @@
     return NO_ERROR;
 }
 
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName,
-                       bool privileged) {
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+                       const char* inFunctionName, bool privileged) {
     const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
     int floatClassification = std::fpclassify(frameRate);
     if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
@@ -637,6 +642,12 @@
         return false;
     }
 
+    if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS &&
+        changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
+        ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
+              changeFrameRateStrategy);
+    }
+
     return true;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6de3e97..2fc9d47 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1729,8 +1729,8 @@
 int Surface::dispatchSetFrameRate(va_list args) {
     float frameRate = static_cast<float>(va_arg(args, double));
     int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
-    bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
-    return setFrameRate(frameRate, compatibility, shouldBeSeamless);
+    int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
+    return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
 }
 
 int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2575,16 +2575,18 @@
     mSurfaceListener->onBuffersDiscarded(discardedBufs);
 }
 
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
+                               int8_t changeFrameRateStrategy) {
     ATRACE_CALL();
     ALOGV("Surface::setFrameRate");
 
-    if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "Surface::setFrameRate")) {
         return BAD_VALUE;
     }
 
     return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
-                                           shouldBeSeamless);
+                                           changeFrameRateStrategy);
 }
 
 status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f12b77e..c888312 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1569,7 +1569,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
         const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
-        bool shouldBeSeamless) {
+        int8_t changeFrameRateStrategy) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1577,7 +1577,8 @@
     }
     // Allow privileged values as well here, those will be ignored by SF if
     // the caller is not privileged
-    if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate",
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "Transaction::setFrameRate",
                            /*privileged=*/true)) {
         mStatus = BAD_VALUE;
         return *this;
@@ -1585,7 +1586,7 @@
     s->what |= layer_state_t::eFrameRateChanged;
     s->frameRate = frameRate;
     s->frameRateCompatibility = compatibility;
-    s->shouldBeSeamless = shouldBeSeamless;
+    s->changeFrameRateStrategy = changeFrameRateStrategy;
     return *this;
 }
 
@@ -2039,6 +2040,17 @@
     return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
 }
 
+status_t SurfaceComposerClient::addHdrLayerInfoListener(
+        const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+    return ComposerService::getComposerService()->addHdrLayerInfoListener(displayToken, listener);
+}
+
+status_t SurfaceComposerClient::removeHdrLayerInfoListener(
+        const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+    return ComposerService::getComposerService()->removeHdrLayerInfoListener(displayToken,
+                                                                             listener);
+}
+
 status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
     return ComposerService::getComposerService()->notifyPowerBoost(boostId);
 }
diff --git a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
new file mode 100644
index 0000000..fc809c4
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IHdrLayerInfoListener {
+    // Callback with the total number of HDR layers, the dimensions of the largest layer,
+    // and a placeholder flags
+    // TODO (b/182312559): Define the flags (likely need an indicator that a UDFPS layer is present)
+    void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9f9ca74..ccd6d4e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -18,6 +18,7 @@
 
 #include <android/gui/DisplayBrightness.h>
 #include <android/gui/IFpsListener.h>
+#include <android/gui/IHdrLayerInfoListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
 #include <binder/IBinder.h>
@@ -431,6 +432,25 @@
                                           const gui::DisplayBrightness& brightness) = 0;
 
     /*
+     * Adds a listener that receives HDR layer information. This is used in combination
+     * with setDisplayBrightness to adjust the display brightness depending on factors such
+     * as whether or not HDR is in use.
+     *
+     * Returns NO_ERROR upon success or NAME_NOT_FOUND if the display is invalid.
+     */
+    virtual status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                             const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+    /*
+     * Removes a listener that was added with addHdrLayerInfoListener.
+     *
+     * Returns NO_ERROR upon success, NAME_NOT_FOUND if the display is invalid, and BAD_VALUE if
+     *     the listener wasn't registered.
+     *
+     */
+    virtual status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                                const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+
+    /*
      * Sends a power boost to the composer. This function is asynchronous.
      *
      * boostId
@@ -466,7 +486,7 @@
      * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
      */
     virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                                  int8_t compatibility, bool shouldBeSeamless) = 0;
+                                  int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
 
     /*
      * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
@@ -578,6 +598,8 @@
         ADD_FPS_LISTENER,
         REMOVE_FPS_LISTENER,
         OVERRIDE_HDR_TYPES,
+        ADD_HDR_LAYER_INFO_LISTENER,
+        REMOVE_HDR_LAYER_INFO_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 9186ed2..706a09c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -127,6 +127,7 @@
     void merge(const layer_state_t& other);
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+    bool hasBufferChanges() const;
 
     struct matrix22_t {
         float dsdx{0};
@@ -207,7 +208,7 @@
     // Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
     float frameRate;
     int8_t frameRateCompatibility;
-    bool shouldBeSeamless;
+    int8_t changeFrameRateStrategy;
 
     // Set by window manager indicating the layer and all its children are
     // in a different orientation than the display. The hint suggests that
@@ -307,10 +308,11 @@
 //
 // @param frameRate the frame rate in Hz
 // @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_*
+// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_*
 // @param functionName calling function or nullptr. Used for logging
 // @param privileged whether caller has unscoped surfaceflinger access
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName,
-                       bool privileged = false);
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+                       const char* functionName, bool privileged = false);
 
 struct CaptureArgs {
     const static int32_t UNSET_UID = -1;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5881221..d22bdaa 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,7 +187,8 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
+    virtual status_t setFrameRate(float frameRate, int8_t compatibility,
+                                  int8_t changeFrameRateStrategy);
     virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
     virtual status_t getExtraBufferCount(int* extraBuffers) const;
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 19d898c..731af58 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -215,6 +215,11 @@
     static status_t setDisplayBrightness(const sp<IBinder>& displayToken,
                                          const gui::DisplayBrightness& brightness);
 
+    static status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                            const sp<gui::IHdrLayerInfoListener>& listener);
+    static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                               const sp<gui::IHdrLayerInfoListener>& listener);
+
     /*
      * Sends a power boost to the composer. This function is asynchronous.
      *
@@ -514,7 +519,7 @@
         Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
 
         Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
-                                  int8_t compatibility, bool shouldBeSeamless);
+                                  int8_t compatibility, int8_t changeFrameRateStrategy);
 
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5ac3f19..751b95a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -819,6 +819,16 @@
         return NO_ERROR;
     }
 
+    status_t addHdrLayerInfoListener(const sp<IBinder>&,
+                                     const sp<gui::IHdrLayerInfoListener>&) override {
+        return NO_ERROR;
+    }
+
+    status_t removeHdrLayerInfoListener(const sp<IBinder>&,
+                                        const sp<gui::IHdrLayerInfoListener>&) override {
+        return NO_ERROR;
+    }
+
     status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
                                        const sp<IBinder>& /*stopLayerHandle*/,
                                        const sp<IRegionSamplingListener>& /*listener*/) override {
@@ -859,7 +869,7 @@
     }
 
     status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
-                          int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
+                          int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
         return NO_ERROR;
     }
 
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index ffcc1cd..31027b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -231,6 +231,13 @@
     mSensors.insert_or_assign(info.type, info);
 }
 
+void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo& info) {
+    if (mBatteries.find(info.id) != mBatteries.end()) {
+        ALOGW("Battery id %d already exists, will be replaced by new battery added.", info.id);
+    }
+    mBatteries.insert_or_assign(info.id, info);
+}
+
 void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) {
     if (mLights.find(info.id) != mLights.end()) {
         ALOGW("Light id %d already exists, will be replaced by new light added.", info.id);
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index c2a3cf1..f7962e0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -110,6 +110,12 @@
                 return true;
             case Type::DRAG:
                 return true;
+            case Type::TIMELINE:
+                const nsecs_t gpuCompletedTime =
+                        body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+                const nsecs_t presentTime =
+                        body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+                return presentTime > gpuCompletedTime;
         }
     }
     return false;
@@ -129,6 +135,8 @@
             return sizeof(Header) + body.capture.size();
         case Type::DRAG:
             return sizeof(Header) + body.drag.size();
+        case Type::TIMELINE:
+            return sizeof(Header) + body.timeline.size();
     }
     return sizeof(Header);
 }
@@ -260,6 +268,11 @@
             msg->body.drag.isExiting = body.drag.isExiting;
             break;
         }
+        case InputMessage::Type::TIMELINE: {
+            msg->body.timeline.eventId = body.timeline.eventId;
+            msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
+            break;
+        }
     }
 }
 
@@ -629,7 +642,7 @@
     return mChannel->sendMessage(&msg);
 }
 
-android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
+android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
     }
@@ -639,16 +652,24 @@
     if (result) {
         return android::base::Error(result);
     }
-    if (msg.header.type != InputMessage::Type::FINISHED) {
-        ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
-              mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
-        return android::base::Error(UNKNOWN_ERROR);
+    if (msg.header.type == InputMessage::Type::FINISHED) {
+        return Finished{
+                .seq = msg.header.seq,
+                .handled = msg.body.finished.handled,
+                .consumeTime = msg.body.finished.consumeTime,
+        };
     }
-    return Finished{
-            .seq = msg.header.seq,
-            .handled = msg.body.finished.handled,
-            .consumeTime = msg.body.finished.consumeTime,
-    };
+
+    if (msg.header.type == InputMessage::Type::TIMELINE) {
+        return Timeline{
+                .inputEventId = msg.body.timeline.eventId,
+                .graphicsTimeline = msg.body.timeline.graphicsTimeline,
+        };
+    }
+
+    ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
+          mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+    return android::base::Error(UNKNOWN_ERROR);
 }
 
 // --- InputConsumer ---
@@ -785,7 +806,8 @@
                 break;
             }
 
-            case InputMessage::Type::FINISHED: {
+            case InputMessage::Type::FINISHED:
+            case InputMessage::Type::TIMELINE: {
                 LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
                                  "InputConsumer!",
                                  NamedEnum::string(mMsg.header.type).c_str());
@@ -1193,6 +1215,24 @@
     return sendUnchainedFinishedSignal(seq, handled);
 }
 
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+                                     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+              ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+              mChannel->getName().c_str(), inputEventId,
+              graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+              graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::TIMELINE;
+    msg.header.seq = 0;
+    msg.body.timeline.eventId = inputEventId;
+    msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+    return mChannel->sendMessage(&msg);
+}
+
 nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
     auto it = mConsumeTimes.find(seq);
     // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
@@ -1399,6 +1439,19 @@
                                                        toString(msg.body.drag.isExiting));
                     break;
                 }
+                case InputMessage::Type::TIMELINE: {
+                    const nsecs_t gpuCompletedTime =
+                            msg.body.timeline
+                                    .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+                    const nsecs_t presentTime =
+                            msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+                    out += android::base::StringPrintf("inputEventId=%" PRId32
+                                                       ", gpuCompletedTime=%" PRId64
+                                                       ", presentTime=%" PRId64,
+                                                       msg.body.timeline.eventId, gpuCompletedTime,
+                                                       presentTime);
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index fc31715..088e00b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -124,13 +124,15 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -264,13 +266,15 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_FALSE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_FALSE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -304,14 +308,16 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
 
-    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -343,13 +349,15 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -385,16 +393,34 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
+TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
+    const int32_t inputEventId = 20;
+    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30;
+    graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40;
+    status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline);
+    ASSERT_EQ(OK, status);
+
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+    const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+    ASSERT_EQ(inputEventId, timeline.inputEventId);
+    ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline);
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 3d80b38..585779e 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -96,6 +96,10 @@
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
   CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
+
+  CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
+  CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
 }
 
 void TestHeaderSize() {
@@ -117,6 +121,9 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
     static_assert(sizeof(InputMessage::Body::Drag) == 16);
+    // Timeline
+    static_assert(GraphicsTimeline::SIZE == 2);
+    static_assert(sizeof(InputMessage::Body::Timeline) == 24);
 }
 
 // --- VerifiedInputEvent ---
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b406a9c..ada689a 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,8 +159,8 @@
 }
 
 int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
-    return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
-        /*shouldBeSeamless*/ true);
+    return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+        ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
 }
 
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -170,12 +170,12 @@
     window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
 }
 
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
-        int8_t compatibility, bool shouldBeSeamless) {
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+        int8_t compatibility, int8_t changeFrameRateStrategy) {
     if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
         return -EINVAL;
     }
-    return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+    return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
 }
 
 /**************************************************************************************************
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 285f2fb..50e9d53 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -247,9 +247,10 @@
 };
 
 /**
- * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
+ * Same as ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
- * See ANativeWindow_setFrameRateWithSeamlessness().
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
  *
  * Available since API level 30.
  */
@@ -267,6 +268,19 @@
  */
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
 
+/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_ChangeFrameRateStrategy {
+    /**
+     * Change the frame rate only if the transition is going to be seamless.
+     */
+    ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0,
+    /**
+     * Change the frame rate even if the transition is going to be non-seamless,
+     * i.e. with visual interruptions for the user.
+     */
+    ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
+} __INTRODUCED_IN(31);
+
 /**
  * Sets the intended frame rate for this window.
  *
@@ -296,17 +310,16 @@
  * compatibility value may influence the system's choice of display refresh
  * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
  *
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
  *
  * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
  * value are invalid.
  */
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
-        int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+        int8_t compatibility, int8_t changeFrameRateStrategy)
+        __INTRODUCED_IN(31);
 
 #ifdef __cplusplus
 };
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7aa2cf4..cc82bb4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1019,9 +1019,9 @@
 }
 
 static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
-                                        int8_t compatibility, bool shouldBeSeamless) {
+                                        int8_t compatibility, int8_t changeFrameRateStrategy) {
     return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
-                           (int)compatibility, (int)shouldBeSeamless);
+                           (int)compatibility, (int)changeFrameRateStrategy);
 }
 
 static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 24d0e3b..988132c 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -47,7 +47,7 @@
     ANativeWindow_setBuffersTransform;
     ANativeWindow_setDequeueTimeout; # apex # introduced=30
     ANativeWindow_setFrameRate; # introduced=30
-    ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
+    ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
     ANativeWindow_setUsage; # llndk
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 06a1722..cd7a86b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,6 +73,7 @@
     bool cleanupPostRender(CleanupMode mode) override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+    void onPrimaryDisplaySizeChanged(ui::Size size) override {}
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7c51f1b..a69d1f0 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -153,6 +153,10 @@
     virtual bool supportsProtectedContent() const = 0;
     virtual bool useProtectedContext(bool useProtectedContext) = 0;
 
+    // Notify RenderEngine of changes to the dimensions of the primary display
+    // so that it can configure its internal caches accordingly.
+    virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0;
+
     // Renders layers for a particular display via GPU composition. This method
     // should be called for every display that needs to be rendered via the GPU.
     // @param display The display-wide settings that should be applied prior to
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 5f75b81..228553d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -55,6 +55,7 @@
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
+    MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
 };
 
 } // namespace mock
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index c7001b9..0798562 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -294,12 +294,13 @@
         mPlaceholderSurface(placeholder),
         mProtectedEGLContext(protectedContext),
         mProtectedPlaceholderSurface(protectedPlaceholder),
+        mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
         mUseColorManagement(args.useColorManagement) {
     sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
-    options.fPreferExternalImagesOverES3 = true;
+    options.fDisableDriverCorrectnessWorkarounds = true;
     options.fDisableDistanceFieldPaths = true;
     options.fPersistentCache = &mSkSLCacheMonitor;
     mGrContext = GrDirectContext::MakeGL(glInterface, options);
@@ -1186,6 +1187,27 @@
     return value;
 }
 
+void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
+    // This cache multiplier was selected based on review of cache sizes relative
+    // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
+    // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
+    // conservative default based on that analysis.
+    const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
+    const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
+
+    // start by resizing the current context
+    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+    grContext->setResourceCacheLimit(maxResourceBytes);
+
+    // if it is possible to switch contexts then we will resize the other context
+    if (useProtectedContext(!mInProtectedContext)) {
+        grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+        grContext->setResourceCacheLimit(maxResourceBytes);
+        // reset back to the initial context that was active when this method was called
+        useProtectedContext(!mInProtectedContext);
+    }
+}
+
 void SkiaGLRenderEngine::dump(std::string& result) {
     const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 7605df9..25557f1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -65,6 +65,7 @@
     bool useProtectedContext(bool useProtectedContext) override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
     void assertShadersCompiled(int numShaders) override;
+    void onPrimaryDisplaySizeChanged(ui::Size size) override;
 
 protected:
     void dump(std::string& result) override;
@@ -109,6 +110,7 @@
     EGLSurface mProtectedPlaceholderSurface;
     BlurFilter* mBlurFilter = nullptr;
 
+    const PixelFormat mDefaultPixelFormat;
     const bool mUseColorManagement;
 
     // Cache of GL textures that we'll store per GraphicBuffer ID
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 6a91c7c..783e37f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -332,6 +332,21 @@
     return resultFuture.get();
 }
 
+void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, size](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
+            instance.onPrimaryDisplaySizeChanged(size);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
 } // namespace threaded
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 7694328..117257a 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -66,6 +66,7 @@
     void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
+    void onPrimaryDisplaySizeChanged(ui::Size size) override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index bdfe04b..538c1d2 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -97,6 +97,34 @@
     return mState != State::INVALID;
 }
 
+status_t FenceTime::wait(int timeout) {
+    // See if we already have a cached value we can return.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return NO_ERROR;
+    }
+
+    // Hold a reference to the fence on the stack in case the class'
+    // reference is removed by another thread. This prevents the
+    // fence from being destroyed until the end of this method, where
+    // we conveniently do not have the lock held.
+    sp<Fence> fence;
+    {
+        // With the lock acquired this time, see if we have the cached
+        // value or if we need to poll the fence.
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (!mFence.get()) {
+            // Another thread set the signal time just before we added the
+            // reference to mFence.
+            return NO_ERROR;
+        }
+        fence = mFence;
+    }
+
+    // Make the system call without the lock held.
+    return fence->wait(timeout);
+}
+
 nsecs_t FenceTime::getSignalTime() {
     // See if we already have a cached value we can return.
     nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ecba7f7..ac75f43 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -112,6 +112,13 @@
     // Returns a snapshot of the FenceTime in its current state.
     Snapshot getSnapshot() const;
 
+    // wait waits for up to timeout milliseconds for the fence to signal.  If
+    // the fence signals then NO_ERROR is returned. If the timeout expires
+    // before the fence signals then -ETIME is returned.  A timeout of
+    // TIMEOUT_NEVER may be used to indicate that the call should wait
+    // indefinitely for the fence to signal.
+    status_t wait(int timeout);
+
     void signalForTest(nsecs_t signalTime);
 
 private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6977396..073455a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3312,15 +3312,21 @@
             bool gotOne = false;
             status_t status = OK;
             for (;;) {
-                Result<InputPublisher::Finished> result =
-                        connection->inputPublisher.receiveFinishedSignal();
+                Result<InputPublisher::ConsumerResponse> result =
+                        connection->inputPublisher.receiveConsumerResponse();
                 if (!result.ok()) {
                     status = result.error().code();
                     break;
                 }
-                const InputPublisher::Finished& finished = *result;
-                d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
-                                             finished.handled, finished.consumeTime);
+
+                if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+                    const InputPublisher::Finished& finish =
+                            std::get<InputPublisher::Finished>(*result);
+                    d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
+                                                 finish.handled, finish.consumeTime);
+                } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+                    // TODO(b/167947340): Report this data to LatencyTracker
+                }
                 gotOne = true;
             }
             if (gotOne) {
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 1af70a4..7468894 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -24,6 +24,7 @@
 cc_library_headers {
     name: "libinputreader_headers",
     export_include_dirs: [
+        "controller",
         "include",
         "mapper",
         "mapper/accumulator",
@@ -35,17 +36,16 @@
     srcs: [
         "EventHub.cpp",
         "InputDevice.cpp",
+        "controller/InputController.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
-        "mapper/BatteryInputMapper.cpp",
         "mapper/CursorInputMapper.cpp",
         "mapper/ExternalStylusInputMapper.cpp",
         "mapper/InputMapper.cpp",
         "mapper/JoystickInputMapper.cpp",
         "mapper/KeyboardInputMapper.cpp",
-        "mapper/LightInputMapper.cpp",
         "mapper/MultiTouchInputMapper.cpp",
         "mapper/RotaryEncoderInputMapper.cpp",
         "mapper/SensorInputMapper.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 07011f5..045d24c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -70,6 +70,20 @@
 static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
 static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
+// Mapping for input battery class node IDs lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
+        {{"capacity", InputBatteryClass::CAPACITY},
+         {"capacity_level", InputBatteryClass::CAPACITY_LEVEL},
+         {"status", InputBatteryClass::STATUS}};
+
+// Mapping for input battery class node names lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<InputBatteryClass, std::string> BATTERY_NODES =
+        {{InputBatteryClass::CAPACITY, "capacity"},
+         {InputBatteryClass::CAPACITY_LEVEL, "capacity_level"},
+         {InputBatteryClass::STATUS, "status"}};
+
 // must be kept in sync with definitions in kernel /drivers/power/supply/power_supply_sysfs.c
 static const std::unordered_map<std::string, int32_t> BATTERY_STATUS =
         {{"Unknown", BATTERY_STATUS_UNKNOWN},
@@ -349,7 +363,7 @@
         virtualKeyMap(nullptr),
         ffEffectPlaying(false),
         ffEffectId(-1),
-        nextLightId(0),
+        miscDevice(nullptr),
         controllerNumber(0),
         enabled(true),
         isVirtual(fd < 0) {}
@@ -540,32 +554,36 @@
 }
 
 // Check the sysfs path for any input device batteries, returns true if battery found.
-bool EventHub::Device::configureBatteryLocked() {
-    if (!sysfsRootPath.has_value()) {
-        return false;
+bool EventHub::MiscDevice::configureBatteryLocked() {
+    nextBatteryId = 0;
+    // Check if device has any battery.
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
+    for (const auto& nodePath : paths) {
+        RawBatteryInfo info;
+        info.id = ++nextBatteryId;
+        info.path = nodePath;
+        info.name = nodePath.filename();
+
+        // Scan the path for all the files
+        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+        const auto& files = allFilesInPath(nodePath);
+        for (const auto& file : files) {
+            const auto it = BATTERY_CLASSES.find(file.filename().string());
+            if (it != BATTERY_CLASSES.end()) {
+                info.flags |= it->second;
+            }
+        }
+        batteryInfos.insert_or_assign(info.id, info);
+        ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
     }
-    // Check if device has any batteries.
-    std::vector<std::filesystem::path> batteryPaths =
-            findSysfsNodes(sysfsRootPath.value(), SysfsClass::POWER_SUPPLY);
-    // We only support single battery for an input device, if multiple batteries exist only the
-    // first one is supported.
-    if (batteryPaths.empty()) {
-        // Set path to be empty
-        sysfsBatteryPath = std::nullopt;
-        return false;
-    }
-    // If a battery exists
-    sysfsBatteryPath = batteryPaths[0];
-    return true;
+    return !batteryInfos.empty();
 }
 
 // Check the sysfs path for any input device lights, returns true if lights found.
-bool EventHub::Device::configureLightsLocked() {
-    if (!sysfsRootPath.has_value()) {
-        return false;
-    }
+bool EventHub::MiscDevice::configureLightsLocked() {
+    nextLightId = 0;
     // Check if device has any lights.
-    const auto& paths = findSysfsNodes(sysfsRootPath.value(), SysfsClass::LEDS);
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
     for (const auto& nodePath : paths) {
         RawLightInfo info;
         info.id = ++nextLightId;
@@ -599,6 +617,7 @@
             }
         }
         lightInfos.insert_or_assign(info.id, info);
+        ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
     }
     return !lightInfos.empty();
 }
@@ -963,42 +982,92 @@
     return Errorf("Device not found or device has no key layout.");
 }
 
+// Gets the battery info map from battery ID to RawBatteryInfo of the miscellaneous device
+// associated with the device ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawBatteryInfo>& EventHub::getBatteryInfoLocked(
+        int32_t deviceId) const {
+    static const std::unordered_map<int32_t, RawBatteryInfo> EMPTY_BATTERY_INFO = {};
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr) {
+        return EMPTY_BATTERY_INFO;
+    }
+    auto it = mMiscDevices.find(device->identifier.descriptor);
+    if (it == mMiscDevices.end()) {
+        return EMPTY_BATTERY_INFO;
+    }
+    return it->second->batteryInfos;
+}
+
+const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+    std::vector<int32_t> batteryIds;
+
+    for (const auto [id, info] : getBatteryInfoLocked(deviceId)) {
+        batteryIds.push_back(id);
+    }
+
+    return batteryIds;
+}
+
+std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+    std::scoped_lock _l(mLock);
+
+    const auto infos = getBatteryInfoLocked(deviceId);
+
+    auto it = infos.find(batteryId);
+    if (it != infos.end()) {
+        return it->second;
+    }
+
+    return std::nullopt;
+}
+
+// Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated
+// with the deivice ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked(
+        int32_t deviceId) const {
+    static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr) {
+        return EMPTY_LIGHT_INFO;
+    }
+    auto it = mMiscDevices.find(device->identifier.descriptor);
+    if (it == mMiscDevices.end()) {
+        return EMPTY_LIGHT_INFO;
+    }
+    return it->second->lightInfos;
+}
+
 const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
     std::vector<int32_t> lightIds;
 
-    if (device != nullptr) {
-        for (const auto [id, info] : device->lightInfos) {
-            lightIds.push_back(id);
-        }
+    for (const auto [id, info] : getLightInfoLocked(deviceId)) {
+        lightIds.push_back(id);
     }
+
     return lightIds;
 }
 
 std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
 
-    if (device != nullptr) {
-        auto it = device->lightInfos.find(lightId);
-        if (it != device->lightInfos.end()) {
-            return it->second;
-        }
+    const auto infos = getLightInfoLocked(deviceId);
+
+    auto it = infos.find(lightId);
+    if (it != infos.end()) {
+        return it->second;
     }
+
     return std::nullopt;
 }
 
 std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        return std::nullopt;
-    }
-
-    auto it = device->lightInfos.find(lightId);
-    if (it == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto it = infos.find(lightId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
     std::string buffer;
@@ -1013,13 +1082,9 @@
         int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        return std::nullopt;
-    }
-
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
         return std::nullopt;
     }
 
@@ -1056,14 +1121,10 @@
 void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        ALOGE("Device Id %d does not exist", deviceId);
-        return;
-    }
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
-        ALOGE("Light Id %d does not exist.", lightId);
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
+        ALOGE("%s lightId %d not found ", __func__, lightId);
         return;
     }
 
@@ -1078,13 +1139,9 @@
                                    std::unordered_map<LightColor, int32_t> intensities) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        ALOGE("Device Id %d does not exist", deviceId);
-        return;
-    }
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
         ALOGE("Light Id %d does not exist.", lightId);
         return;
     }
@@ -1352,51 +1409,56 @@
     return nullptr;
 }
 
-std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
-    std::string buffer;
 
-    if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+    const auto infos = getBatteryInfoLocked(deviceId);
+    auto it = infos.find(batteryId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
+    std::string buffer;
 
     // Some devices report battery capacity as an integer through the "capacity" file
-    if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
+    if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
+                               &buffer)) {
         return std::stoi(base::Trim(buffer));
     }
 
     // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
     // These values are taken from kernel source code include/linux/power_supply.h
-    if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
+    if (base::ReadFileToString(it->second.path /
+                                       BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
+                               &buffer)) {
         // Remove any white space such as trailing new line
-        const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
-        if (it != BATTERY_LEVEL.end()) {
-            return it->second;
+        const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer));
+        if (levelIt != BATTERY_LEVEL.end()) {
+            return levelIt->second;
         }
     }
+
     return std::nullopt;
 }
 
-std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
-    std::string buffer;
-
-    if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+    const auto infos = getBatteryInfoLocked(deviceId);
+    auto it = infos.find(batteryId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
+    std::string buffer;
 
-    if (!base::ReadFileToString(device->sysfsBatteryPath.value() / "status", &buffer)) {
+    if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS),
+                                &buffer)) {
         ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
         return std::nullopt;
     }
 
     // Remove white space like trailing new line
-    const auto it = BATTERY_STATUS.find(base::Trim(buffer));
-
-    if (it != BATTERY_STATUS.end()) {
-        return it->second;
+    const auto statusIt = BATTERY_STATUS.find(base::Trim(buffer));
+    if (statusIt != BATTERY_STATUS.end()) {
+        return statusIt->second;
     }
 
     return std::nullopt;
@@ -1879,11 +1941,24 @@
     // Load the configuration file for the device.
     device->loadConfigurationLocked();
 
-    // Grab the device's sysfs path
-    device->sysfsRootPath = getSysfsRootPath(devicePath.c_str());
-    // find related components
-    bool hasBattery = device->configureBatteryLocked();
-    bool hasLights = device->configureLightsLocked();
+    bool hasBattery = false;
+    bool hasLights = false;
+    // Check the sysfs root path
+    std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
+    if (sysfsRootPath.has_value()) {
+        std::shared_ptr<MiscDevice> miscDevice;
+        auto it = mMiscDevices.find(device->identifier.descriptor);
+        if (it == mMiscDevices.end()) {
+            miscDevice = std::make_shared<MiscDevice>(sysfsRootPath.value());
+        } else {
+            miscDevice = it->second;
+        }
+        hasBattery = miscDevice->configureBatteryLocked();
+        hasLights = miscDevice->configureLightsLocked();
+
+        device->miscDevice = miscDevice;
+        mMiscDevices.insert_or_assign(device->identifier.descriptor, std::move(miscDevice));
+    }
 
     // Figure out the kinds of events the device reports.
     device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
@@ -2254,6 +2329,12 @@
     mClosingDevices.push_back(std::move(mDevices[device.id]));
 
     mDevices.erase(device.id);
+    // If all devices with the descriptor have been removed then the miscellaneous device should
+    // be removed too.
+    std::string descriptor = device.identifier.descriptor;
+    if (getDeviceByDescriptorLocked(descriptor) == nullptr) {
+        mMiscDevices.erase(descriptor);
+    }
 }
 
 status_t EventHub::readNotifyLocked() {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index dd1abeb..f935c36 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -21,13 +21,12 @@
 #include <input/Flags.h>
 #include <algorithm>
 
-#include "BatteryInputMapper.h"
 #include "CursorInputMapper.h"
 #include "ExternalStylusInputMapper.h"
+#include "InputController.h"
 #include "InputReaderContext.h"
 #include "JoystickInputMapper.h"
 #include "KeyboardInputMapper.h"
-#include "LightInputMapper.h"
 #include "MultiTouchInputMapper.h"
 #include "RotaryEncoderInputMapper.h"
 #include "SensorInputMapper.h"
@@ -131,6 +130,9 @@
     }
 
     for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
+    if (mController) {
+        mController->dump(dump);
+    }
 }
 
 void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
@@ -162,22 +164,10 @@
         mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
     }
 
-    // Battery-like devices. Only one battery mapper for each EventHub device.
-    if (classes.test(InputDeviceClass::BATTERY)) {
-        InputDeviceInfo deviceInfo;
-        getDeviceInfo(&deviceInfo);
-        if (!deviceInfo.hasBattery()) {
-            mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr));
-        }
-    }
-
-    // Light-containing devices. Only one light mapper for each EventHub device.
-    if (classes.test(InputDeviceClass::LIGHT)) {
-        InputDeviceInfo deviceInfo;
-        getDeviceInfo(&deviceInfo);
-        if (deviceInfo.getLightIds().empty()) {
-            mappers.push_back(std::make_unique<LightInputMapper>(*contextPtr));
-        }
+    // Battery-like devices or light-containing devices.
+    // InputController will be created with associated EventHub device.
+    if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
+        mController = std::make_unique<InputController>(*contextPtr);
     }
 
     // Keyboard-like devices.
@@ -409,6 +399,10 @@
                               mHasMic);
     for_each_mapper(
             [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
+
+    if (mController) {
+        mController->populateDeviceInfo(outDeviceInfo);
+    }
 }
 
 int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -510,39 +504,30 @@
     for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
 }
 
+// TODO b/180733860 support multiple battery in API and remove this.
+constexpr int32_t DEFAULT_BATTERY_ID = 1;
 std::optional<int32_t> InputDevice::getBatteryCapacity() {
-    return first_in_mappers<int32_t>(
-            [](InputMapper& mapper) { return mapper.getBatteryCapacity(); });
+    return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
 }
 
 std::optional<int32_t> InputDevice::getBatteryStatus() {
-    return first_in_mappers<int32_t>([](InputMapper& mapper) { return mapper.getBatteryStatus(); });
+    return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt;
 }
 
 bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
-    bool success = true;
-    for_each_mapper([&success, lightId, color](InputMapper& mapper) {
-        success &= mapper.setLightColor(lightId, color);
-    });
-    return success;
+    return mController ? mController->setLightColor(lightId, color) : false;
 }
 
 bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) {
-    bool success = true;
-    for_each_mapper([&success, lightId, playerId](InputMapper& mapper) {
-        success &= mapper.setLightPlayerId(lightId, playerId);
-    });
-    return success;
+    return mController ? mController->setLightPlayerId(lightId, playerId) : false;
 }
 
 std::optional<int32_t> InputDevice::getLightColor(int32_t lightId) {
-    return first_in_mappers<int32_t>(
-            [lightId](InputMapper& mapper) { return mapper.getLightColor(lightId); });
+    return mController ? mController->getLightColor(lightId) : std::nullopt;
 }
 
 std::optional<int32_t> InputDevice::getLightPlayerId(int32_t lightId) {
-    return first_in_mappers<int32_t>(
-            [lightId](InputMapper& mapper) { return mapper.getLightPlayerId(lightId); });
+    return mController ? mController->getLightPlayerId(lightId) : std::nullopt;
 }
 
 int32_t InputDevice::getMetaState() {
diff --git a/services/inputflinger/reader/controller/InputController.cpp b/services/inputflinger/reader/controller/InputController.cpp
new file mode 100644
index 0000000..45266fb
--- /dev/null
+++ b/services/inputflinger/reader/controller/InputController.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2021 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 <locale>
+#include <regex>
+
+#include "../Macros.h"
+
+#include "InputController.h"
+#include "input/NamedEnum.h"
+
+// Log detailed debug messages about input device lights.
+static constexpr bool DEBUG_LIGHT_DETAILS = false;
+
+namespace android {
+
+static inline int32_t getAlpha(int32_t color) {
+    return (color >> 24) & 0xff;
+}
+
+static inline int32_t getRed(int32_t color) {
+    return (color >> 16) & 0xff;
+}
+
+static inline int32_t getGreen(int32_t color) {
+    return (color >> 8) & 0xff;
+}
+
+static inline int32_t getBlue(int32_t color) {
+    return color & 0xff;
+}
+
+static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
+    return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
+}
+
+/**
+ * Input controller owned by InputReader device, implements the native API for querying input
+ * lights, getting and setting the lights brightness and color, by interacting with EventHub
+ * devices.
+ */
+InputController::InputController(InputDeviceContext& deviceContext)
+      : mDeviceContext(deviceContext) {
+    configureBattries();
+    configureLights();
+}
+
+InputController::~InputController() {}
+
+std::optional<std::int32_t> InputController::Light::getRawLightBrightness(int32_t rawLightId) {
+    std::optional<RawLightInfo> rawInfoOpt = context.getRawLightInfo(rawLightId);
+    if (!rawInfoOpt.has_value()) {
+        return std::nullopt;
+    }
+    std::optional<int32_t> brightnessOpt = context.getLightBrightness(rawLightId);
+    if (!brightnessOpt.has_value()) {
+        return std::nullopt;
+    }
+    int brightness = brightnessOpt.value();
+
+    // If the light node doesn't have max brightness, use the default max brightness.
+    int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
+    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
+    if (rawMaxBrightness != MAX_BRIGHTNESS) {
+        brightness = brightness * ratio;
+    }
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+              brightness, ratio);
+    }
+    return brightness;
+}
+
+void InputController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
+    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
+    if (!rawInfo.has_value()) {
+        return;
+    }
+    // If the light node doesn't have max brightness, use the default max brightness.
+    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
+    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
+    if (rawMaxBrightness != MAX_BRIGHTNESS) {
+        brightness = ceil(brightness / ratio);
+    }
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+              brightness, ratio);
+    }
+    context.setLightBrightness(rawLightId, brightness);
+}
+
+bool InputController::SingleLight::setLightColor(int32_t color) {
+    int32_t brightness = getAlpha(color);
+    setRawLightBrightness(rawId, brightness);
+
+    return true;
+}
+
+bool InputController::RgbLight::setLightColor(int32_t color) {
+    // Compose color value as per:
+    // https://developer.android.com/reference/android/graphics/Color?hl=en
+    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+    // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
+    // MAX_BRIGHTNESS.
+    brightness = getAlpha(color);
+    int32_t red = 0;
+    int32_t green = 0;
+    int32_t blue = 0;
+    if (brightness > 0) {
+        float ratio = MAX_BRIGHTNESS / brightness;
+        red = ceil(getRed(color) / ratio);
+        green = ceil(getGreen(color) / ratio);
+        blue = ceil(getBlue(color) / ratio);
+    }
+    setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
+    setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
+    setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
+    if (rawGlobalId.has_value()) {
+        setRawLightBrightness(rawGlobalId.value(), brightness);
+    }
+
+    return true;
+}
+
+bool InputController::MultiColorLight::setLightColor(int32_t color) {
+    std::unordered_map<LightColor, int32_t> intensities;
+    intensities.emplace(LightColor::RED, getRed(color));
+    intensities.emplace(LightColor::GREEN, getGreen(color));
+    intensities.emplace(LightColor::BLUE, getBlue(color));
+
+    context.setLightIntensities(rawId, intensities);
+    setRawLightBrightness(rawId, getAlpha(color));
+    return true;
+}
+
+std::optional<int32_t> InputController::SingleLight::getLightColor() {
+    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+    if (!brightness.has_value()) {
+        return std::nullopt;
+    }
+
+    return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
+}
+
+std::optional<int32_t> InputController::RgbLight::getLightColor() {
+    // If the Alpha component is zero, then return color 0.
+    if (brightness == 0) {
+        return 0;
+    }
+    // Compose color value as per:
+    // https://developer.android.com/reference/android/graphics/Color?hl=en
+    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+    std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
+    std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
+    std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
+    // If we can't get brightness for any of the RGB light
+    if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
+        return std::nullopt;
+    }
+
+    // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
+    // value, scale it back to return the nominal color value.
+    float ratio = MAX_BRIGHTNESS / brightness;
+    int32_t red = round(redOr.value() * ratio);
+    int32_t green = round(greenOr.value() * ratio);
+    int32_t blue = round(blueOr.value() * ratio);
+
+    if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
+        // Previously stored brightness isn't valid for current LED values, so just reset to max
+        // brightness since an app couldn't have provided these values in the first place.
+        red = redOr.value();
+        green = greenOr.value();
+        blue = blueOr.value();
+        brightness = MAX_BRIGHTNESS;
+    }
+
+    return toArgb(brightness, red, green, blue);
+}
+
+std::optional<int32_t> InputController::MultiColorLight::getLightColor() {
+    auto ret = context.getLightIntensities(rawId);
+    if (!ret.has_value()) {
+        return std::nullopt;
+    }
+    std::unordered_map<LightColor, int32_t> intensities = ret.value();
+    // Get red, green, blue colors
+    int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
+                           intensities.at(LightColor::GREEN) /* green */,
+                           intensities.at(LightColor::BLUE) /* blue */);
+    // Get brightness
+    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+    if (brightness.has_value()) {
+        return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
+    }
+    return std::nullopt;
+}
+
+bool InputController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
+    if (rawLightIds.find(playerId) == rawLightIds.end()) {
+        return false;
+    }
+    for (const auto& [id, rawId] : rawLightIds) {
+        if (playerId == id) {
+            setRawLightBrightness(rawId, MAX_BRIGHTNESS);
+        } else {
+            setRawLightBrightness(rawId, 0);
+        }
+    }
+    return true;
+}
+
+std::optional<int32_t> InputController::PlayerIdLight::getLightPlayerId() {
+    for (const auto& [id, rawId] : rawLightIds) {
+        std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+        if (brightness.has_value() && brightness.value() > 0) {
+            return id;
+        }
+    }
+    return std::nullopt;
+}
+
+void InputController::SingleLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void InputController::PlayerIdLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
+    dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
+    for (const auto& [id, rawId] : rawLightIds) {
+        dump += StringPrintf("id %d -> %d ", id, rawId);
+    }
+    dump += "\n";
+}
+
+void InputController::RgbLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+    dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
+                         rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+    if (rawGlobalId.has_value()) {
+        dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
+    }
+    dump += "\n";
+}
+
+void InputController::MultiColorLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void InputController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+    // TODO: b/180733860 Remove this after enabling multi-battery
+    if (!mBatteries.empty()) {
+        deviceInfo->setHasBattery(true);
+    }
+
+    for (const auto& [batteryId, battery] : mBatteries) {
+        InputDeviceBatteryInfo batteryInfo(battery->name, battery->id);
+        deviceInfo->addBatteryInfo(batteryInfo);
+    }
+
+    for (const auto& [lightId, light] : mLights) {
+        // Input device light doesn't support ordinal, always pass 1.
+        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+        deviceInfo->addLightInfo(lightInfo);
+    }
+}
+
+void InputController::dump(std::string& dump) {
+    dump += INDENT2 "Input Controller:\n";
+    if (!mLights.empty()) {
+        dump += INDENT3 "Lights:\n";
+        for (const auto& [lightId, light] : mLights) {
+            dump += StringPrintf(INDENT4 "Id: %d", lightId);
+            dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
+            dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+            light->dump(dump);
+        }
+    }
+    // Dump raw lights
+    dump += INDENT3 "RawLights:\n";
+    dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
+    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+    // Map from raw light id to raw light info
+    std::unordered_map<int32_t, RawLightInfo> rawInfos;
+    for (const auto& rawId : rawLightIds) {
+        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        dump += StringPrintf(INDENT4 "%d", rawId);
+        dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
+        dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
+        dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
+        dump += StringPrintf(INDENT4 "%d\n",
+                             getDeviceContext().getLightBrightness(rawId).value_or(-1));
+    }
+
+    if (!mBatteries.empty()) {
+        dump += INDENT3 "Batteries:\n";
+        for (const auto& [batteryId, battery] : mBatteries) {
+            dump += StringPrintf(INDENT4 "Id: %d", batteryId);
+            dump += StringPrintf(INDENT4 "Name: %s", battery->name.c_str());
+            dump += getBatteryCapacity(batteryId).has_value()
+                    ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity(batteryId).value())
+                    : StringPrintf(INDENT3 "Capacity: Unknown");
+
+            std::string status;
+            switch (getBatteryStatus(batteryId).value_or(BATTERY_STATUS_UNKNOWN)) {
+                case BATTERY_STATUS_CHARGING:
+                    status = "Charging";
+                    break;
+                case BATTERY_STATUS_DISCHARGING:
+                    status = "Discharging";
+                    break;
+                case BATTERY_STATUS_NOT_CHARGING:
+                    status = "Not charging";
+                    break;
+                case BATTERY_STATUS_FULL:
+                    status = "Full";
+                    break;
+                default:
+                    status = "Unknown";
+            }
+            dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
+        }
+    }
+}
+
+void InputController::configureBattries() {
+    // Check raw batteries
+    const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
+
+    for (const auto& rawId : rawBatteryIds) {
+        std::optional<RawBatteryInfo> rawInfo = getDeviceContext().getRawBatteryInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        std::unique_ptr<Battery> battery =
+                std::make_unique<Battery>(getDeviceContext(), rawInfo->name, rawInfo->id);
+        mBatteries.insert_or_assign(rawId, std::move(battery));
+    }
+}
+
+void InputController::configureLights() {
+    bool hasRedLed = false;
+    bool hasGreenLed = false;
+    bool hasBlueLed = false;
+    std::optional<int32_t> rawGlobalId = std::nullopt;
+    // Player ID light common name string
+    std::string playerIdName;
+    // Raw RGB color to raw light ID
+    std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
+    // Map from player Id to raw light Id
+    std::unordered_map<int32_t, int32_t> playerIdLightIds;
+
+    // Check raw lights
+    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+    // Map from raw light id to raw light info
+    std::unordered_map<int32_t, RawLightInfo> rawInfos;
+    for (const auto& rawId : rawLightIds) {
+        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        rawInfos.insert_or_assign(rawId, rawInfo.value());
+        // Check if this is a group LEDs for player ID
+        std::regex lightPattern("([a-z]+)([0-9]+)");
+        std::smatch results;
+        if (std::regex_match(rawInfo->name, results, lightPattern)) {
+            std::string commonName = results[1].str();
+            int32_t playerId = std::stoi(results[2]);
+            if (playerIdLightIds.empty()) {
+                playerIdName = commonName;
+                playerIdLightIds.insert_or_assign(playerId, rawId);
+            } else {
+                // Make sure the player ID leds have common string name
+                if (playerIdName.compare(commonName) == 0 &&
+                    playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
+                    playerIdLightIds.insert_or_assign(playerId, rawId);
+                }
+            }
+        }
+        // Check if this is an LED of RGB light
+        if (rawInfo->flags.test(InputLightClass::RED)) {
+            hasRedLed = true;
+            rawRgbIds.emplace(LightColor::RED, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::GREEN)) {
+            hasGreenLed = true;
+            rawRgbIds.emplace(LightColor::GREEN, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::BLUE)) {
+            hasBlueLed = true;
+            rawRgbIds.emplace(LightColor::BLUE, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
+            rawGlobalId = rawId;
+        }
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
+                  rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
+        }
+    }
+
+    // Construct a player ID light
+    if (playerIdLightIds.size() > 1) {
+        std::unique_ptr<Light> light =
+                std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
+                                                playerIdLightIds);
+        mLights.insert_or_assign(light->id, std::move(light));
+        // Remove these raw lights from raw light info as they've been used to compose a
+        // Player ID light, so we do not expose these raw lights as single lights.
+        for (const auto& [playerId, rawId] : playerIdLightIds) {
+            rawInfos.erase(rawId);
+        }
+    }
+    // Construct a RGB light for composed RGB light
+    if (hasRedLed && hasGreenLed && hasBlueLed) {
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
+                  rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+        }
+        std::unique_ptr<Light> light =
+                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+        mLights.insert_or_assign(light->id, std::move(light));
+        // Remove from raw light info as they've been composed a RBG light.
+        rawInfos.erase(rawRgbIds.at(LightColor::RED));
+        rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
+        rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
+        if (rawGlobalId.has_value()) {
+            rawInfos.erase(rawGlobalId.value());
+        }
+    }
+
+    // Check the rest of raw light infos
+    for (const auto& [rawId, rawInfo] : rawInfos) {
+        // If the node is multi-color led, construct a MULTI_COLOR light
+        if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
+            rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
+            if (DEBUG_LIGHT_DETAILS) {
+                ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+            }
+            std::unique_ptr<Light> light =
+                    std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+                                                      rawInfo.id);
+            mLights.insert_or_assign(light->id, std::move(light));
+            continue;
+        }
+        // Construct a single LED light
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+        }
+        std::unique_ptr<Light> light =
+                std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+                                              rawInfo.id);
+
+        mLights.insert_or_assign(light->id, std::move(light));
+    }
+}
+
+std::optional<int32_t> InputController::getBatteryCapacity(int batteryId) {
+    return getDeviceContext().getBatteryCapacity(batteryId);
+}
+
+std::optional<int32_t> InputController::getBatteryStatus(int batteryId) {
+    return getDeviceContext().getBatteryStatus(batteryId);
+}
+
+bool InputController::setLightColor(int32_t lightId, int32_t color) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return false;
+    }
+    auto& light = it->second;
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
+              NamedEnum::string(light->type).c_str(), color);
+    }
+    return light->setLightColor(color);
+}
+
+std::optional<int32_t> InputController::getLightColor(int32_t lightId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return std::nullopt;
+    }
+    auto& light = it->second;
+    std::optional<int32_t> color = light->getLightColor();
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
+              NamedEnum::string(light->type).c_str(), color.value_or(0));
+    }
+    return color;
+}
+
+bool InputController::setLightPlayerId(int32_t lightId, int32_t playerId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return false;
+    }
+    auto& light = it->second;
+    return light->setLightPlayerId(playerId);
+}
+
+std::optional<int32_t> InputController::getLightPlayerId(int32_t lightId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return std::nullopt;
+    }
+    auto& light = it->second;
+    return light->getLightPlayerId();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/controller/InputController.h
similarity index 81%
rename from services/inputflinger/reader/mapper/LightInputMapper.h
rename to services/inputflinger/reader/controller/InputController.h
index 43141b8..d4222e2 100644
--- a/services/inputflinger/reader/mapper/LightInputMapper.h
+++ b/services/inputflinger/reader/controller/InputController.h
@@ -14,35 +14,49 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
-#define _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
 
-#include "InputMapper.h"
+#include "InputControllerInterface.h"
 
 namespace android {
 
-class LightInputMapper : public InputMapper {
+class InputController : public InputControllerInterface {
     // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
     /* Number of colors : {red, green, blue} */
     static constexpr size_t COLOR_NUM = 3;
     static constexpr int32_t MAX_BRIGHTNESS = 0xff;
 
 public:
-    explicit LightInputMapper(InputDeviceContext& deviceContext);
-    ~LightInputMapper() override;
+    explicit InputController(InputDeviceContext& deviceContext);
+    ~InputController() override;
 
-    uint32_t getSources() override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
     bool setLightColor(int32_t lightId, int32_t color) override;
     bool setLightPlayerId(int32_t lightId, int32_t playerId) override;
     std::optional<int32_t> getLightColor(int32_t lightId) override;
     std::optional<int32_t> getLightPlayerId(int32_t lightId) override;
+    std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override;
+    std::optional<int32_t> getBatteryStatus(int32_t batteryId) override;
 
 private:
+    inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+    inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+
+    InputDeviceContext& mDeviceContext;
+    void configureLights();
+    void configureBattries();
+
+    struct Battery {
+        explicit Battery(InputDeviceContext& context, const std::string& name, int32_t id)
+              : context(context), name(name), id(id) {}
+        virtual ~Battery() {}
+        InputDeviceContext& context;
+        std::string name;
+        int32_t id;
+    };
+
     struct Light {
         explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
                        InputDeviceLightType type)
@@ -128,8 +142,11 @@
 
     // Light map from light ID to Light
     std::unordered_map<int32_t, std::unique_ptr<Light>> mLights;
+
+    // Battery map from battery ID to battery
+    std::unordered_map<int32_t, std::unique_ptr<Battery>> mBatteries;
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/InputControllerInterface.h b/services/inputflinger/reader/controller/InputControllerInterface.h
new file mode 100644
index 0000000..504eded
--- /dev/null
+++ b/services/inputflinger/reader/controller/InputControllerInterface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H
+#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+/* An input controller manages the miscellaneous devices associated with the input device,
+ * like the sysfs based battery and light class devices.
+ *
+ */
+class InputControllerInterface {
+public:
+    InputControllerInterface() {}
+    virtual ~InputControllerInterface() {}
+
+    // Interface methods for Battery
+    virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
+    virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
+
+    // Interface methods for Light
+    virtual bool setLightColor(int32_t lightId, int32_t color) = 0;
+    virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) = 0;
+    virtual std::optional<int32_t> getLightColor(int32_t lightId) = 0;
+    virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) = 0;
+
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) = 0;
+    virtual void dump(std::string& dump) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 2afaa85..c970c8b 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -173,6 +173,15 @@
     MAX_BRIGHTNESS = 0x00000080,
 };
 
+enum class InputBatteryClass : uint32_t {
+    /* The input device battery has capacity node. */
+    CAPACITY = 0x00000001,
+    /* The input device battery has capacity_level node. */
+    CAPACITY_LEVEL = 0x00000002,
+    /* The input device battery has status node. */
+    STATUS = 0x00000004,
+};
+
 /* Describes a raw light. */
 struct RawLightInfo {
     int32_t id;
@@ -183,6 +192,14 @@
     std::filesystem::path path;
 };
 
+/* Describes a raw battery. */
+struct RawBatteryInfo {
+    int32_t id;
+    std::string name;
+    Flags<InputBatteryClass> flags;
+    std::filesystem::path path;
+};
+
 /*
  * Gets the class that owns an axis, in cases where multiple classes might claim
  * the same axis for different purposes.
@@ -263,6 +280,12 @@
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
     virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
                                                                               int32_t absCode) = 0;
+    // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+    // containing the raw info of the sysfs node structure.
+    virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0;
+    virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                            int32_t BatteryId) = 0;
+
     // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node,
     // containing the raw info of the sysfs node structure.
     virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0;
@@ -307,10 +330,11 @@
     virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
 
     /* Query battery level. */
-    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const = 0;
+    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+                                                      int32_t batteryId) const = 0;
 
     /* Query battery status. */
-    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) const = 0;
+    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const = 0;
 
     /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
     virtual void requestReopenDevices() = 0;
@@ -435,6 +459,10 @@
     base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
             int32_t deviceId, int32_t absCode) override final;
 
+    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final;
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t BatteryId) override final;
+
     const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final;
 
     std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final;
@@ -485,9 +513,11 @@
 
     void monitor() override final;
 
-    std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const override final;
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+                                              int32_t batteryId) const override final;
 
-    std::optional<int32_t> getBatteryStatus(int32_t deviceId) const override final;
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId,
+                                            int32_t batteryId) const override final;
 
     bool isDeviceEnabled(int32_t deviceId) override final;
 
@@ -498,6 +528,22 @@
     ~EventHub() override;
 
 private:
+    struct MiscDevice {
+        // The device descriptor from evdev device the misc device associated with.
+        std::string descriptor;
+        // The sysfs root path of the misc device.
+        std::filesystem::path sysfsRootPath;
+
+        int32_t nextBatteryId;
+        int32_t nextLightId;
+        std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
+        std::unordered_map<int32_t, RawLightInfo> lightInfos;
+        explicit MiscDevice(std::filesystem::path sysfsRootPath)
+              : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
+        bool configureBatteryLocked();
+        bool configureLightsLocked();
+    };
+
     struct Device {
         int fd; // may be -1 if device is closed
         const int32_t id;
@@ -527,12 +573,7 @@
         bool ffEffectPlaying;
         int16_t ffEffectId; // initially -1
 
-        // The paths are invalid when they're std::nullopt
-        std::optional<std::filesystem::path> sysfsRootPath;
-        std::optional<std::filesystem::path> sysfsBatteryPath;
-        // maps from light id to light info
-        std::unordered_map<int32_t, RawLightInfo> lightInfos;
-        int32_t nextLightId;
+        std::shared_ptr<MiscDevice> miscDevice;
 
         int32_t controllerNumber;
 
@@ -563,8 +604,6 @@
         void setLedForControllerLocked();
         status_t mapLed(int32_t led, int32_t* outScanCode) const;
         void setLedStateLocked(int32_t led, bool on);
-        bool configureBatteryLocked();
-        bool configureLightsLocked();
     };
 
     /**
@@ -616,6 +655,12 @@
     void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
                                               Flags<InputDeviceClass> classes) REQUIRES(mLock);
 
+    const std::unordered_map<int32_t, RawBatteryInfo>& getBatteryInfoLocked(int32_t deviceId) const
+            REQUIRES(mLock);
+
+    const std::unordered_map<int32_t, RawLightInfo>& getLightInfoLocked(int32_t deviceId) const
+            REQUIRES(mLock);
+
     // Protect all internal state.
     mutable std::mutex mLock;
 
@@ -645,6 +690,11 @@
     std::vector<std::unique_ptr<Device>> mOpeningDevices;
     std::vector<std::unique_ptr<Device>> mClosingDevices;
 
+    // Map from std::string descriptor, to a shared_ptr of a miscellaneous device associated with
+    // the input device. The descriptor is the same from the EventHub device which it associates
+    // with.
+    std::unordered_map<std::string, std::shared_ptr<MiscDevice>> mMiscDevices;
+
     bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
     bool mNeedToScanDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 863cd41..5d56f5a 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -33,6 +33,8 @@
 
 namespace android {
 
+class InputController;
+class InputControllerInterface;
 class InputDeviceContext;
 class InputMapper;
 
@@ -131,6 +133,20 @@
         return *mapper;
     }
 
+    // construct and add a controller to the input device
+    template <class T>
+    T& addController(int32_t eventHubId) {
+        // ensure a device entry exists for this eventHubId
+        addEventHubDevice(eventHubId, false);
+
+        // create controller
+        auto& devicePair = mDevices[eventHubId];
+        auto& deviceContext = devicePair.first;
+
+        mController = std::make_unique<T>(*deviceContext);
+        return *(reinterpret_cast<T*>(mController.get()));
+    }
+
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -143,7 +159,10 @@
     // map from eventHubId to device context and mappers
     using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
     using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+    // Map from EventHub ID to pair of device context and vector of mapper.
     std::unordered_map<int32_t, DevicePair> mDevices;
+    // Misc devices controller for lights, battery, etc.
+    std::unique_ptr<InputControllerInterface> mController;
 
     uint32_t mSources;
     bool mIsExternal;
@@ -319,11 +338,21 @@
 
     inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
 
-    inline std::optional<int32_t> getBatteryCapacity() {
-        return mEventHub->getBatteryCapacity(mId);
+    inline const std::vector<int32_t> getRawBatteryIds() {
+        return mEventHub->getRawBatteryIds(mId);
     }
 
-    inline std::optional<int32_t> getBatteryStatus() { return mEventHub->getBatteryStatus(mId); }
+    inline std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t batteryId) {
+        return mEventHub->getRawBatteryInfo(mId, batteryId);
+    }
+
+    inline std::optional<int32_t> getBatteryCapacity(int32_t batteryId) {
+        return mEventHub->getBatteryCapacity(mId, batteryId);
+    }
+
+    inline std::optional<int32_t> getBatteryStatus(int32_t batteryId) {
+        return mEventHub->getBatteryStatus(mId, batteryId);
+    }
 
     inline bool hasAbsoluteAxis(int32_t code) const {
         RawAbsoluteAxisInfo info;
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
deleted file mode 100644
index e4fb3a6..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 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 "../Macros.h"
-
-#include "BatteryInputMapper.h"
-
-namespace android {
-
-BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
-
-uint32_t BatteryInputMapper::getSources() {
-    return AINPUT_SOURCE_UNKNOWN;
-}
-
-void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    info->setHasBattery(true);
-}
-
-void BatteryInputMapper::process(const RawEvent* rawEvent) {}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryCapacity() {
-    return getDeviceContext().getBatteryCapacity();
-}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryStatus() {
-    return getDeviceContext().getBatteryStatus();
-}
-
-void BatteryInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Battery Input Mapper:\n";
-    dump += getBatteryCapacity().has_value()
-            ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity().value())
-            : StringPrintf(INDENT3 "Capacity: Unknown");
-
-    std::string status;
-    switch (getBatteryStatus().value_or(BATTERY_STATUS_UNKNOWN)) {
-        case BATTERY_STATUS_CHARGING:
-            status = "Charging";
-            break;
-        case BATTERY_STATUS_DISCHARGING:
-            status = "Discharging";
-            break;
-        case BATTERY_STATUS_NOT_CHARGING:
-            status = "Not charging";
-            break;
-        case BATTERY_STATUS_FULL:
-            status = "Full";
-            break;
-        default:
-            status = "Unknown";
-    }
-    dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
-}
-
-} // namespace android
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.h b/services/inputflinger/reader/mapper/BatteryInputMapper.h
deleted file mode 100644
index 4fe373e..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-#ifndef _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
-#define _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
-
-#include "InputMapper.h"
-
-namespace android {
-
-class BatteryInputMapper : public InputMapper {
-public:
-    explicit BatteryInputMapper(InputDeviceContext& deviceContext);
-    virtual ~BatteryInputMapper(){};
-
-    uint32_t getSources() override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    void process(const RawEvent* rawEvent) override;
-
-    std::optional<int32_t> getBatteryCapacity() override;
-    std::optional<int32_t> getBatteryStatus() override;
-
-    void dump(std::string& dump) override;
-};
-
-} // namespace android
-
-#endif // _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.cpp b/services/inputflinger/reader/mapper/LightInputMapper.cpp
deleted file mode 100644
index be1f722..0000000
--- a/services/inputflinger/reader/mapper/LightInputMapper.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2021 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 <locale>
-#include <regex>
-
-#include "../Macros.h"
-
-#include "LightInputMapper.h"
-#include "input/NamedEnum.h"
-
-// Log detailed debug messages about input device lights.
-static constexpr bool DEBUG_LIGHT_DETAILS = false;
-
-namespace android {
-
-static inline int32_t getAlpha(int32_t color) {
-    return (color >> 24) & 0xff;
-}
-
-static inline int32_t getRed(int32_t color) {
-    return (color >> 16) & 0xff;
-}
-
-static inline int32_t getGreen(int32_t color) {
-    return (color >> 8) & 0xff;
-}
-
-static inline int32_t getBlue(int32_t color) {
-    return color & 0xff;
-}
-
-static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
-    return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
-}
-
-/**
- * Light input mapper owned by InputReader device, implements the native API for querying input
- * lights, getting and setting the lights brightness and color, by interacting with EventHub
- * devices.
- * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class
- * like lights and battery.
- */
-LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
-
-LightInputMapper::~LightInputMapper() {}
-
-std::optional<std::int32_t> LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) {
-    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
-    std::optional<int32_t> ret = context.getLightBrightness(rawLightId);
-    if (!rawInfo.has_value() || !ret.has_value()) {
-        return std::nullopt;
-    }
-    int brightness = ret.value();
-
-    // If the light node doesn't have max brightness, use the default max brightness.
-    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
-    // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
-    if (rawMaxBrightness != MAX_BRIGHTNESS) {
-        brightness = brightness * ratio;
-    }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
-    return brightness;
-}
-
-void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
-    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
-    if (!rawInfo.has_value()) {
-        return;
-    }
-    // If the light node doesn't have max brightness, use the default max brightness.
-    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
-    // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
-    if (rawMaxBrightness != MAX_BRIGHTNESS) {
-        brightness = ceil(brightness / ratio);
-    }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
-    context.setLightBrightness(rawLightId, brightness);
-}
-
-bool LightInputMapper::SingleLight::setLightColor(int32_t color) {
-    int32_t brightness = getAlpha(color);
-    setRawLightBrightness(rawId, brightness);
-
-    return true;
-}
-
-bool LightInputMapper::RgbLight::setLightColor(int32_t color) {
-    // Compose color value as per:
-    // https://developer.android.com/reference/android/graphics/Color?hl=en
-    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
-    // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
-    // MAX_BRIGHTNESS.
-    brightness = getAlpha(color);
-    int32_t red = 0;
-    int32_t green = 0;
-    int32_t blue = 0;
-    if (brightness > 0) {
-        float ratio = MAX_BRIGHTNESS / brightness;
-        red = ceil(getRed(color) / ratio);
-        green = ceil(getGreen(color) / ratio);
-        blue = ceil(getBlue(color) / ratio);
-    }
-    setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
-    setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
-    setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
-    if (rawGlobalId.has_value()) {
-        setRawLightBrightness(rawGlobalId.value(), brightness);
-    }
-
-    return true;
-}
-
-bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) {
-    std::unordered_map<LightColor, int32_t> intensities;
-    intensities.emplace(LightColor::RED, getRed(color));
-    intensities.emplace(LightColor::GREEN, getGreen(color));
-    intensities.emplace(LightColor::BLUE, getBlue(color));
-
-    context.setLightIntensities(rawId, intensities);
-    setRawLightBrightness(rawId, getAlpha(color));
-    return true;
-}
-
-std::optional<int32_t> LightInputMapper::SingleLight::getLightColor() {
-    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-    if (!brightness.has_value()) {
-        return std::nullopt;
-    }
-
-    return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
-}
-
-std::optional<int32_t> LightInputMapper::RgbLight::getLightColor() {
-    // If the Alpha component is zero, then return color 0.
-    if (brightness == 0) {
-        return 0;
-    }
-    // Compose color value as per:
-    // https://developer.android.com/reference/android/graphics/Color?hl=en
-    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
-    std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
-    std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
-    std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
-    // If we can't get brightness for any of the RGB light
-    if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
-        return std::nullopt;
-    }
-
-    // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
-    // value, scale it back to return the nominal color value.
-    float ratio = MAX_BRIGHTNESS / brightness;
-    int32_t red = round(redOr.value() * ratio);
-    int32_t green = round(greenOr.value() * ratio);
-    int32_t blue = round(blueOr.value() * ratio);
-
-    if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
-        // Previously stored brightness isn't valid for current LED values, so just reset to max
-        // brightness since an app couldn't have provided these values in the first place.
-        red = redOr.value();
-        green = greenOr.value();
-        blue = blueOr.value();
-        brightness = MAX_BRIGHTNESS;
-    }
-
-    return toArgb(brightness, red, green, blue);
-}
-
-std::optional<int32_t> LightInputMapper::MultiColorLight::getLightColor() {
-    auto ret = context.getLightIntensities(rawId);
-    if (!ret.has_value()) {
-        return std::nullopt;
-    }
-    std::unordered_map<LightColor, int32_t> intensities = ret.value();
-    // Get red, green, blue colors
-    int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
-                           intensities.at(LightColor::GREEN) /* green */,
-                           intensities.at(LightColor::BLUE) /* blue */);
-    // Get brightness
-    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-    if (brightness.has_value()) {
-        return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
-    }
-    return std::nullopt;
-}
-
-bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) {
-    if (rawLightIds.find(playerId) == rawLightIds.end()) {
-        return false;
-    }
-    for (const auto& [id, rawId] : rawLightIds) {
-        if (playerId == id) {
-            setRawLightBrightness(rawId, MAX_BRIGHTNESS);
-        } else {
-            setRawLightBrightness(rawId, 0);
-        }
-    }
-    return true;
-}
-
-std::optional<int32_t> LightInputMapper::PlayerIdLight::getLightPlayerId() {
-    for (const auto& [id, rawId] : rawLightIds) {
-        std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-        if (brightness.has_value() && brightness.value() > 0) {
-            return id;
-        }
-    }
-    return std::nullopt;
-}
-
-void LightInputMapper::SingleLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-void LightInputMapper::PlayerIdLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
-    dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
-    for (const auto& [id, rawId] : rawLightIds) {
-        dump += StringPrintf("id %d -> %d ", id, rawId);
-    }
-    dump += "\n";
-}
-
-void LightInputMapper::RgbLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-    dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
-                         rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
-    if (rawGlobalId.has_value()) {
-        dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
-    }
-    dump += "\n";
-}
-
-void LightInputMapper::MultiColorLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-uint32_t LightInputMapper::getSources() {
-    return AINPUT_SOURCE_UNKNOWN;
-}
-
-void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    for (const auto& [lightId, light] : mLights) {
-        // Input device light doesn't support ordinal, always pass 1.
-        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
-        info->addLightInfo(lightInfo);
-    }
-}
-
-void LightInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Light Input Mapper:\n";
-    dump += INDENT3 "Lights:\n";
-    for (const auto& [lightId, light] : mLights) {
-        dump += StringPrintf(INDENT4 "Id: %d", lightId);
-        dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
-        dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
-        light->dump(dump);
-    }
-    // Dump raw lights
-    dump += INDENT3 "RawLights:\n";
-    dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
-    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
-    // Map from raw light id to raw light info
-    std::unordered_map<int32_t, RawLightInfo> rawInfos;
-    for (const auto& rawId : rawLightIds) {
-        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
-        if (!rawInfo.has_value()) {
-            continue;
-        }
-        dump += StringPrintf(INDENT4 "%d", rawId);
-        dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
-        dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
-        dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
-        dump += StringPrintf(INDENT4 "%d\n",
-                             getDeviceContext().getLightBrightness(rawId).value_or(-1));
-    }
-}
-
-void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                 uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    if (!changes) { // first time only
-        bool hasRedLed = false;
-        bool hasGreenLed = false;
-        bool hasBlueLed = false;
-        std::optional<int32_t> rawGlobalId = std::nullopt;
-        // Player ID light common name string
-        std::string playerIdName;
-        // Raw RGB color to raw light ID
-        std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
-        // Map from player Id to raw light Id
-        std::unordered_map<int32_t, int32_t> playerIdLightIds;
-        mLights.clear();
-
-        // Check raw lights
-        const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
-        // Map from raw light id to raw light info
-        std::unordered_map<int32_t, RawLightInfo> rawInfos;
-        for (const auto& rawId : rawLightIds) {
-            std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
-            if (!rawInfo.has_value()) {
-                continue;
-            }
-            rawInfos.insert_or_assign(rawId, rawInfo.value());
-            // Check if this is a group LEDs for player ID
-            std::regex lightPattern("([a-z]+)([0-9]+)");
-            std::smatch results;
-            if (std::regex_match(rawInfo->name, results, lightPattern)) {
-                std::string commonName = results[1].str();
-                int32_t playerId = std::stoi(results[2]);
-                if (playerIdLightIds.empty()) {
-                    playerIdName = commonName;
-                    playerIdLightIds.insert_or_assign(playerId, rawId);
-                } else {
-                    // Make sure the player ID leds have common string name
-                    if (playerIdName.compare(commonName) == 0 &&
-                        playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
-                        playerIdLightIds.insert_or_assign(playerId, rawId);
-                    }
-                }
-            }
-            // Check if this is an LED of RGB light
-            if (rawInfo->flags.test(InputLightClass::RED)) {
-                hasRedLed = true;
-                rawRgbIds.emplace(LightColor::RED, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::GREEN)) {
-                hasGreenLed = true;
-                rawRgbIds.emplace(LightColor::GREEN, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::BLUE)) {
-                hasBlueLed = true;
-                rawRgbIds.emplace(LightColor::BLUE, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
-                rawGlobalId = rawId;
-            }
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id,
-                      rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
-                      rawInfo->flags.string().c_str());
-            }
-        }
-
-        // Construct a player ID light
-        if (playerIdLightIds.size() > 1) {
-            std::unique_ptr<Light> light =
-                    std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
-                                                    playerIdLightIds);
-            mLights.insert_or_assign(light->id, std::move(light));
-            // Remove these raw lights from raw light info as they've been used to compose a
-            // Player ID light, so we do not expose these raw lights as single lights.
-            for (const auto& [playerId, rawId] : playerIdLightIds) {
-                rawInfos.erase(rawId);
-            }
-        }
-        // Construct a RGB light for composed RGB light
-        if (hasRedLed && hasGreenLed && hasBlueLed) {
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
-                      rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
-            }
-            std::unique_ptr<Light> light = std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
-                                                                      rawRgbIds, rawGlobalId);
-            mLights.insert_or_assign(light->id, std::move(light));
-            // Remove from raw light info as they've been composed a RBG light.
-            rawInfos.erase(rawRgbIds.at(LightColor::RED));
-            rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
-            rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
-            if (rawGlobalId.has_value()) {
-                rawInfos.erase(rawGlobalId.value());
-            }
-        }
-
-        // Check the rest of raw light infos
-        for (const auto& [rawId, rawInfo] : rawInfos) {
-            // If the node is multi-color led, construct a MULTI_COLOR light
-            if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
-                rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
-                if (DEBUG_LIGHT_DETAILS) {
-                    ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-                }
-                std::unique_ptr<Light> light =
-                        std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name,
-                                                          ++mNextId, rawInfo.id);
-                mLights.insert_or_assign(light->id, std::move(light));
-                continue;
-            }
-            // Construct a single LED light
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-            }
-            std::unique_ptr<Light> light =
-                    std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
-                                                  rawInfo.id);
-
-            mLights.insert_or_assign(light->id, std::move(light));
-        }
-    }
-}
-
-void LightInputMapper::reset(nsecs_t when) {
-    InputMapper::reset(when);
-}
-
-void LightInputMapper::process(const RawEvent* rawEvent) {}
-
-bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return false;
-    }
-    auto& light = it->second;
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color);
-    }
-    return light->setLightColor(color);
-}
-
-std::optional<int32_t> LightInputMapper::getLightColor(int32_t lightId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return std::nullopt;
-    }
-    auto& light = it->second;
-    std::optional<int32_t> color = light->getLightColor();
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color.value_or(0));
-    }
-    return color;
-}
-
-bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return false;
-    }
-    auto& light = it->second;
-    return light->setLightPlayerId(playerId);
-}
-
-std::optional<int32_t> LightInputMapper::getLightPlayerId(int32_t lightId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return std::nullopt;
-    }
-    auto& light = it->second;
-    return light->getLightPlayerId();
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 76f6e74..b62fce4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -742,6 +742,11 @@
         ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
+    void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+        const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+        ASSERT_EQ(OK, status);
+    }
+
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                       std::optional<int32_t> expectedDisplayId,
                       std::optional<int32_t> expectedFlags) {
@@ -1054,6 +1059,11 @@
         mInputReceiver->finishEvent(sequenceNum);
     }
 
+    void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+        mInputReceiver->sendTimeline(inputEventId, timeline);
+    }
+
     InputEvent* consume() {
         if (mInputReceiver == nullptr) {
             return nullptr;
@@ -1970,6 +1980,21 @@
     secondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+    graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+
+    window->sendTimeline(1 /*inputEventId*/, graphicsTimeline);
+    window->assertNoEvents();
+    mDispatcher->waitForIdle();
+}
+
 class FakeMonitorReceiver {
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d69bb6a..3d99a6b 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#include <BatteryInputMapper.h>
 #include <CursorInputMapper.h>
+#include <InputController.h>
 #include <InputDevice.h>
 #include <InputMapper.h>
 #include <InputReader.h>
 #include <InputReaderBase.h>
 #include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
-#include <LightInputMapper.h>
 #include <MultiTouchInputMapper.h>
 #include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
@@ -71,6 +70,7 @@
 static constexpr int32_t FIRST_TRACKING_ID = 0;
 static constexpr int32_t SECOND_TRACKING_ID = 1;
 static constexpr int32_t THIRD_TRACKING_ID = 2;
+static constexpr int32_t DEFAULT_BATTERY = 1;
 static constexpr int32_t BATTERY_STATUS = 4;
 static constexpr int32_t BATTERY_CAPACITY = 66;
 static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
@@ -895,9 +895,19 @@
 
     std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
 
-    std::optional<int32_t> getBatteryCapacity(int32_t) const override { return BATTERY_CAPACITY; }
+    std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
+        return BATTERY_CAPACITY;
+    }
 
-    std::optional<int32_t> getBatteryStatus(int32_t) const override { return BATTERY_STATUS; }
+    std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
+        return BATTERY_STATUS;
+    }
+
+    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
+
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+        return std::nullopt;
+    }
 
     const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
         std::vector<int32_t> ids;
@@ -2005,56 +2015,25 @@
     ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
 }
 
-class FakeBatteryInputMapper : public FakeInputMapper {
-public:
-    FakeBatteryInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : FakeInputMapper(deviceContext, sources) {}
+// --- FakeInputController ---
 
-    std::optional<int32_t> getBatteryCapacity() override {
-        return getDeviceContext().getBatteryCapacity();
+class FakeInputController : public InputControllerInterface {
+public:
+    FakeInputController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+
+    ~FakeInputController() override {}
+
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
+
+    void dump(std::string& dump) override {}
+
+    std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override {
+        return getDeviceContext().getBatteryCapacity(batteryId);
     }
 
-    std::optional<int32_t> getBatteryStatus() override {
-        return getDeviceContext().getBatteryStatus();
+    std::optional<int32_t> getBatteryStatus(int32_t batteryId) override {
+        return getDeviceContext().getBatteryStatus(batteryId);
     }
-};
-
-TEST_F(InputReaderTest, BatteryGetCapacity) {
-    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
-    constexpr int32_t eventHubId = 1;
-    const char* DEVICE_LOCATION = "BLUETOOTH";
-    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeBatteryInputMapper& mapper =
-            device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->pushNextDevice(device);
-
-    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
-    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
-}
-
-TEST_F(InputReaderTest, BatteryGetStatus) {
-    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
-    constexpr int32_t eventHubId = 1;
-    const char* DEVICE_LOCATION = "BLUETOOTH";
-    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeBatteryInputMapper& mapper =
-            device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->pushNextDevice(device);
-
-    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
-    ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
-}
-
-class FakeLightInputMapper : public FakeInputMapper {
-public:
-    FakeLightInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : FakeInputMapper(deviceContext, sources) {}
 
     bool setLightColor(int32_t lightId, int32_t color) override {
         getDeviceContext().setLightBrightness(lightId, color >> 24);
@@ -2068,16 +2047,54 @@
         }
         return result.value() << 24;
     }
+
+    bool setLightPlayerId(int32_t lightId, int32_t playerId) override { return true; }
+
+    std::optional<int32_t> getLightPlayerId(int32_t lightId) override { return std::nullopt; }
+
+private:
+    InputDeviceContext& mDeviceContext;
+    inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+    inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
 };
 
+TEST_F(InputReaderTest, BatteryGetCapacity) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
+    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+}
+
+TEST_F(InputReaderTest, BatteryGetStatus) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
+    ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+}
+
 TEST_F(InputReaderTest, LightGetColor) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeLightInputMapper& mapper =
-            device->addMapper<FakeLightInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
     mReader->pushNextDevice(device);
     RawLightInfo info = {.id = 1,
                          .name = "Mono",
@@ -2088,8 +2105,9 @@
     mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55);
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
 
+    ASSERT_TRUE(controller.setLightColor(1 /* lightId */, LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(1 /* lightId */), LIGHT_BRIGHTNESS);
     ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS));
     ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS);
 }
@@ -2989,154 +3007,6 @@
     mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
 }
 
-// --- BatteryInputMapperTest ---
-class BatteryInputMapperTest : public InputMapperTest {
-protected:
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY); }
-};
-
-TEST_F(BatteryInputMapperTest, GetSources) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryCapacity) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_TRUE(mapper.getBatteryCapacity());
-    ASSERT_EQ(mapper.getBatteryCapacity().value_or(-1), BATTERY_CAPACITY);
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryStatus) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_TRUE(mapper.getBatteryStatus());
-    ASSERT_EQ(mapper.getBatteryStatus().value_or(-1), BATTERY_STATUS);
-}
-
-// --- LightInputMapperTest ---
-class LightInputMapperTest : public InputMapperTest {
-protected:
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); }
-};
-
-TEST_F(LightInputMapperTest, GetSources) {
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(LightInputMapperTest, SingleLight) {
-    RawLightInfo infoSingle = {.id = 1,
-                               .name = "Mono",
-                               .maxBrightness = 255,
-                               .flags = InputLightClass::BRIGHTNESS,
-                               .path = ""};
-    mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_BRIGHTNESS));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
-}
-
-TEST_F(LightInputMapperTest, RGBLight) {
-    RawLightInfo infoRed = {.id = 1,
-                            .name = "red",
-                            .maxBrightness = 255,
-                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
-                            .path = ""};
-    RawLightInfo infoGreen = {.id = 2,
-                              .name = "green",
-                              .maxBrightness = 255,
-                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
-                              .path = ""};
-    RawLightInfo infoBlue = {.id = 3,
-                             .name = "blue",
-                             .maxBrightness = 255,
-                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
-                             .path = ""};
-    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
-    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
-    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, MultiColorRGBLight) {
-    RawLightInfo infoColor = {.id = 1,
-                              .name = "red",
-                              .maxBrightness = 255,
-                              .flags = InputLightClass::BRIGHTNESS |
-                                      InputLightClass::MULTI_INTENSITY |
-                                      InputLightClass::MULTI_INDEX,
-                              .path = ""};
-
-    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, PlayerIdLight) {
-    RawLightInfo info1 = {.id = 1,
-                          .name = "player1",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info2 = {.id = 2,
-                          .name = "player2",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info3 = {.id = 3,
-                          .name = "player3",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info4 = {.id = 4,
-                          .name = "player4",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
-    mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
-    mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
-    mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
-
-    ASSERT_FALSE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_TRUE(mapper.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
-    ASSERT_EQ(mapper.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
-}
-
 // --- KeyboardInputMapperTest ---
 
 class KeyboardInputMapperTest : public InputMapperTest {
@@ -8728,4 +8598,216 @@
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
+// --- InputControllerTest ---
+
+class InputControllerTest : public testing::Test {
+protected:
+    static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
+    static const int32_t DEVICE_ID;
+    static const int32_t DEVICE_GENERATION;
+    static const int32_t DEVICE_CONTROLLER_NUMBER;
+    static const Flags<InputDeviceClass> DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
+
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
+    sp<FakeInputReaderPolicy> mFakePolicy;
+    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<InstrumentedInputReader> mReader;
+    std::shared_ptr<InputDevice> mDevice;
+
+    virtual void SetUp(Flags<InputDeviceClass> classes) {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
+        mFakePolicy = new FakeInputReaderPolicy();
+        mFakeListener = new TestInputListener();
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
+        mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+    }
+
+    void SetUp() override { SetUp(DEVICE_CLASSES); }
+
+    void TearDown() override {
+        mFakeListener.clear();
+        mFakePolicy.clear();
+    }
+
+    void configureDevice(uint32_t changes) {
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mReader->requestRefreshConfiguration(changes);
+            mReader->loopOnce();
+        }
+        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+    }
+
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location, int32_t eventHubId,
+                                           Flags<InputDeviceClass> classes) {
+        InputDeviceIdentifier identifier;
+        identifier.name = name;
+        identifier.location = location;
+        std::shared_ptr<InputDevice> device =
+                std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+                                              identifier);
+        mReader->pushNextDevice(device);
+        mFakeEventHub->addDevice(eventHubId, name, classes);
+        mReader->loopOnce();
+        return device;
+    }
+
+    template <class T, typename... Args>
+    T& addControllerAndConfigure(Args... args) {
+        T& controller = mDevice->addController<T>(EVENTHUB_ID, args...);
+
+        return controller;
+    }
+};
+
+const char* InputControllerTest::DEVICE_NAME = "device";
+const char* InputControllerTest::DEVICE_LOCATION = "BLUETOOTH";
+const int32_t InputControllerTest::DEVICE_ID = END_RESERVED_ID + 1000;
+const int32_t InputControllerTest::DEVICE_GENERATION = 2;
+const int32_t InputControllerTest::DEVICE_CONTROLLER_NUMBER = 0;
+const Flags<InputDeviceClass> InputControllerTest::DEVICE_CLASSES =
+        Flags<InputDeviceClass>(0); // not needed for current tests
+const int32_t InputControllerTest::EVENTHUB_ID = 1;
+
+// --- BatteryControllerTest ---
+class BatteryControllerTest : public InputControllerTest {
+protected:
+    void SetUp() override {
+        InputControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
+    }
+};
+
+TEST_F(BatteryControllerTest, GetBatteryCapacity) {
+    InputController& controller = addControllerAndConfigure<InputController>();
+
+    ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+}
+
+TEST_F(BatteryControllerTest, GetBatteryStatus) {
+    InputController& controller = addControllerAndConfigure<InputController>();
+
+    ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+}
+
+// --- LightControllerTest ---
+class LightControllerTest : public InputControllerTest {
+protected:
+    void SetUp() override { InputControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); }
+};
+
+TEST_F(LightControllerTest, SingleLight) {
+    RawLightInfo infoSingle = {.id = 1,
+                               .name = "Mono",
+                               .maxBrightness = 255,
+                               .flags = InputLightClass::BRIGHTNESS,
+                               .path = ""};
+    mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
+
+    InputController& controller = addControllerAndConfigure<InputController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, RGBLight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+    InputController& controller = addControllerAndConfigure<InputController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBLight) {
+    RawLightInfo infoColor = {.id = 1,
+                              .name = "red",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS |
+                                      InputLightClass::MULTI_INTENSITY |
+                                      InputLightClass::MULTI_INDEX,
+                              .path = ""};
+
+    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+    InputController& controller = addControllerAndConfigure<InputController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+    RawLightInfo info1 = {.id = 1,
+                          .name = "player1",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info2 = {.id = 2,
+                          .name = "player2",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info3 = {.id = 3,
+                          .name = "player3",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info4 = {.id = 4,
+                          .name = "player4",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+    mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+    mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+    mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+    InputController& controller = addControllerAndConfigure<InputController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
+
+    ASSERT_FALSE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_TRUE(controller.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
+    ASSERT_EQ(controller.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
+}
+
 } // namespace android
diff --git a/services/memtrackproxy/Android.bp b/services/memtrackproxy/Android.bp
new file mode 100644
index 0000000..7d78f3b
--- /dev/null
+++ b/services/memtrackproxy/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libmemtrackproxy",
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libcutils",
+        "libutils",
+        "android.hardware.memtrack@1.0",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    srcs: [
+        "MemtrackProxy.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    local_include_dirs: [
+        "include/memtrackproxy",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.memtrack@1.0",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+}
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
new file mode 100644
index 0000000..8da6e89
--- /dev/null
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 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 "MemtrackProxy.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <private/android_filesystem_config.h>
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+// Check Memtrack Flags
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_ACCOUNTED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_ACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_UNACCOUNTED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_UNACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED_PSS) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED_PSS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::PRIVATE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_PRIVATE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SYSTEM) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SYSTEM));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::DEDICATED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_DEDICATED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::NONSECURE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_NONSECURE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SECURE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SECURE));
+
+// Check Memtrack Types
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::OTHER) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::OTHER));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GL) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::GL));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GRAPHICS) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::GRAPHICS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::MULTIMEDIA) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::MULTIMEDIA));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::CAMERA) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::CAMERA));
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+                                                   V1_aidl::MemtrackRecord* out) {
+    // Convert uint64_t to int64_t (long in AIDL). AIDL doesn't support unsigned types.
+    if (in.sizeInBytes > std::numeric_limits<int64_t>::max() || in.sizeInBytes < 0) {
+        return false;
+    }
+    out->sizeInBytes = static_cast<int64_t>(in.sizeInBytes);
+
+    // It's ok to just assign directly, since this is a bitmap.
+    out->flags = in.flags;
+    return true;
+}
+
+sp<V1_0_hidl::IMemtrack> MemtrackProxy::MemtrackHidlInstance() {
+    return V1_0_hidl::IMemtrack::getService();
+}
+
+std::shared_ptr<V1_aidl::IMemtrack> MemtrackProxy::MemtrackAidlInstance() {
+    const auto instance = std::string() + V1_aidl::IMemtrack::descriptor + "/default";
+    bool declared = AServiceManager_isDeclared(instance.c_str());
+    if (!declared) {
+        return nullptr;
+    }
+    ndk::SpAIBinder memtrack_binder =
+            ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+    return V1_aidl::IMemtrack::fromBinder(memtrack_binder);
+}
+
+bool MemtrackProxy::CheckUid(uid_t calling_uid) {
+    // Allow AID_SHELL for adb shell dumpsys meminfo
+    return calling_uid == AID_SYSTEM || calling_uid == AID_ROOT || calling_uid == AID_SHELL;
+}
+
+bool MemtrackProxy::CheckPid(pid_t calling_pid, pid_t request_pid) {
+    return calling_pid == request_pid;
+}
+
+MemtrackProxy::MemtrackProxy()
+      : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
+        memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+
+ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
+                                            std::vector<MemtrackRecord>* _aidl_return) {
+    if (pid < 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    if (!MemtrackProxy::CheckPid(AIBinder_getCallingPid(), pid) &&
+        !MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                EX_SECURITY,
+                "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getMemory() for PIDs other "
+                "than the calling PID");
+    }
+
+    if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+        type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    _aidl_return->clear();
+
+    if (memtrack_aidl_instance_ ||
+        (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+        return memtrack_aidl_instance_->getMemory(pid, type, _aidl_return);
+    } else if (memtrack_hidl_instance_ ||
+               (memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance())) {
+        ndk::ScopedAStatus aidl_status;
+
+        Return<void> ret = memtrack_hidl_instance_->getMemory(
+                pid, static_cast<V1_0_hidl::MemtrackType>(type),
+                [&_aidl_return, &aidl_status](V1_0_hidl::MemtrackStatus status,
+                                              hidl_vec<V1_0_hidl::MemtrackRecord> records) {
+                    switch (status) {
+                        case V1_0_hidl::MemtrackStatus::SUCCESS:
+                            aidl_status = ndk::ScopedAStatus::ok();
+                            break;
+                        case V1_0_hidl::MemtrackStatus::MEMORY_TRACKING_NOT_SUPPORTED:
+                            [[fallthrough]];
+                        case V1_0_hidl::MemtrackStatus::TYPE_NOT_SUPPORTED:
+                            [[fallthrough]];
+                        default:
+                            aidl_status =
+                                    ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+                            return;
+                    }
+
+                    _aidl_return->resize(records.size());
+                    for (size_t i = 0; i < records.size(); i++) {
+                        if (!translate(records[i], &(*_aidl_return)[i])) {
+                            aidl_status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                                    EX_SERVICE_SPECIFIC,
+                                    "Failed to convert HIDL MemtrackRecord to AIDL");
+                            return;
+                        }
+                    }
+                });
+
+        // Check HIDL return
+        if (!ret.isOk()) {
+            const char* err_msg = "HIDL Memtrack::getMemory() failed";
+            aidl_status =
+                    ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC, err_msg);
+            LOG(ERROR) << err_msg << ": " << ret.description();
+        }
+
+        return aidl_status;
+    }
+
+    return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+                                                            "Memtrack HAL service not available");
+}
+
+ndk::ScopedAStatus MemtrackProxy::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+    if (!MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SECURITY,
+                "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getGpuDeviceInfo()");
+    }
+
+    _aidl_return->clear();
+
+    if (memtrack_aidl_instance_ ||
+        (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+        return memtrack_aidl_instance_->getGpuDeviceInfo(_aidl_return);
+    }
+
+    return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+                                                            "Memtrack HAL service not available");
+}
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
new file mode 100644
index 0000000..5ac1fbf
--- /dev/null
+++ b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/hardware/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+
+using ::android::sp;
+
+namespace V1_0_hidl = ::android::hardware::memtrack::V1_0;
+namespace V1_aidl = ::aidl::android::hardware::memtrack;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+                                                   V1_aidl::MemtrackRecord* out);
+
+class MemtrackProxy : public BnMemtrack {
+public:
+    MemtrackProxy();
+    ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+                                 std::vector<MemtrackRecord>* _aidl_return) override;
+    ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+
+private:
+    static sp<V1_0_hidl::IMemtrack> MemtrackHidlInstance();
+    static std::shared_ptr<V1_aidl::IMemtrack> MemtrackAidlInstance();
+    static bool CheckUid(uid_t calling_uid);
+    static bool CheckPid(pid_t calling_pid, pid_t request_pid);
+
+    sp<V1_0_hidl::IMemtrack> memtrack_hidl_instance_;
+    std::shared_ptr<V1_aidl::IMemtrack> memtrack_aidl_instance_;
+};
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/test/Android.bp b/services/memtrackproxy/test/Android.bp
new file mode 100644
index 0000000..f943761
--- /dev/null
+++ b/services/memtrackproxy/test/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "memtrackproxy_test",
+    srcs: [
+        "MemtrackProxyTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libmemtrackproxy",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/services/memtrackproxy/test/MemtrackProxyTest.cpp b/services/memtrackproxy/test/MemtrackProxyTest.cpp
new file mode 100644
index 0000000..16dfba0
--- /dev/null
+++ b/services/memtrackproxy/test/MemtrackProxyTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+
+class MemtrackProxyTest : public ::testing::Test {
+public:
+    virtual void SetUp() override {
+        const char* kMemtrackProxyService = "memtrack.proxy";
+        auto memtrackProxyBinder =
+                ndk::SpAIBinder(AServiceManager_waitForService(kMemtrackProxyService));
+        memtrack_proxy_ = IMemtrack::fromBinder(memtrackProxyBinder);
+        ASSERT_NE(memtrack_proxy_, nullptr);
+    }
+
+    std::shared_ptr<IMemtrack> memtrack_proxy_;
+};
+
+TEST_F(MemtrackProxyTest, GetMemoryForInvalidPid) {
+    int pid = -1;
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForCallingPid) {
+    int pid = getpid();
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        EXPECT_TRUE(status.isOk());
+    }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForOtherPid) {
+    int pid = 1;
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        // Test is run as root
+        EXPECT_TRUE(status.isOk());
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index ca9ff7c..c769e97 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,9 +7,6 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-subdirs = [
-    "hidl"
-]
 cc_library_shared {
     name: "libsensorservice",
 
@@ -52,6 +49,7 @@
         "libhardware_legacy",
         "libutils",
         "liblog",
+        "libbatterystats_aidl",
         "libbinder",
         "libsensor",
         "libsensorprivacy",
diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h
index 43a750c..09eb2c1 100644
--- a/services/sensorservice/BatteryService.h
+++ b/services/sensorservice/BatteryService.h
@@ -17,7 +17,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
 #include <utils/Singleton.h>
 
 namespace android {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 470059a..b976eb5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -157,6 +157,7 @@
         "FpsReporter.cpp",
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
+        "HdrLayerInfoReporter.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 8f1aef0..be9bce0 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -329,6 +329,37 @@
     mRefreshPending = false;
     return hasReadyFrame();
 }
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+    const auto frameRateCompatibility = [frameRate] {
+        switch (frameRate.type) {
+            case Layer::FrameRateCompatibility::Default:
+                return FrameRateCompatibility::Default;
+            case Layer::FrameRateCompatibility::ExactOrMultiple:
+                return FrameRateCompatibility::ExactOrMultiple;
+            default:
+                return FrameRateCompatibility::Undefined;
+        }
+    }();
+
+    const auto seamlessness = [frameRate] {
+        switch (frameRate.seamlessness) {
+            case scheduler::Seamlessness::OnlySeamless:
+                return Seamlessness::ShouldBeSeamless;
+            case scheduler::Seamlessness::SeamedAndSeamless:
+                return Seamlessness::NotRequired;
+            default:
+                return Seamlessness::Undefined;
+        }
+    }();
+
+    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+                                       .frameRateCompatibility = frameRateCompatibility,
+                                       .seamlessness = seamlessness};
+}
+} // namespace
 
 bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
@@ -381,7 +412,9 @@
     const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
     if (presentFence->isValid()) {
         mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                              refreshRate, renderRate);
+                                              refreshRate, renderRate,
+                                              frameRateToSetFrameRateVotePayload(
+                                                      mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -393,7 +426,9 @@
         // timestamp instead.
         const nsecs_t actualPresentTime = display->getRefreshTimestamp();
         mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                             refreshRate, renderRate);
+                                             refreshRate, renderRate,
+                                             frameRateToSetFrameRateVotePayload(
+                                                     mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                actualPresentTime,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 7a10769..09e3cd9 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -431,10 +431,12 @@
 }
 
 bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
-    // The acquire fences of BufferStateLayers have already signaled before they are set
-    mCallbackHandleAcquireTime = fence->getSignalTime();
-
     mCurrentState.acquireFence = fence;
+    mCurrentState.acquireFenceTime = std::make_unique<FenceTime>(fence);
+
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = mCurrentState.acquireFenceTime->getSignalTime();
+
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -690,7 +692,8 @@
         // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
         // are processing the next state.
         addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
-                                          mDrawingState.acquireFence->getSignalTime(), latchTime);
+                                          mDrawingState.acquireFenceTime->getSignalTime(),
+                                          latchTime);
     }
 
     mCurrentStateModified = false;
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2ae49fa..44b33ef 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -48,7 +48,7 @@
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
-        ALOGE("failed to get buffer, invalid buffer id");
+        ALOGV("failed to get buffer, invalid buffer id");
         return false;
     }
 
@@ -150,7 +150,7 @@
 
     ClientCacheBuffer* buf = nullptr;
     if (!getBuffer(cacheId, &buf)) {
-        ALOGE("failed to register erased recipient, could not retrieve buffer");
+        ALOGV("failed to register erased recipient, could not retrieve buffer");
         return false;
     }
     buf->recipients.insert(recipient);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8402149..a45be8a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -55,6 +55,16 @@
     std::vector<uint8_t> value;
 
     std::string dumpAsString() const;
+
+    struct Hasher {
+        size_t operator()(const GenericLayerMetadataEntry& entry) const {
+            size_t hash = 0;
+            for (const auto value : entry.value) {
+                hashCombineSingleHashed(hash, value);
+            }
+            return hash;
+        }
+    };
 };
 
 inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
@@ -70,6 +80,8 @@
 
 /*
  * Used by LayerFE::getCompositionState
+ * Note that fields that affect HW composer state may need to be mirrored into
+ * android::compositionengine::impl::planner::LayerState
  */
 struct LayerFECompositionState {
     // If set to true, forces client composition on all output layers until
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index f113c34..ae88e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -55,7 +55,6 @@
     std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
 
     void dump(std::string&) const override;
-
     virtual FloatRect calculateOutputSourceCrop() const;
     virtual Rect calculateOutputDisplayFrame() const;
     virtual uint32_t calculateOutputRelativeBufferTransform(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index a3e84e2..4c065ec 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -16,10 +16,7 @@
 
 #pragma once
 
-#include <cstdint>
-#include <optional>
-#include <string>
-
+#include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <renderengine/Mesh.h>
 #include <ui/FloatRect.h>
@@ -27,6 +24,10 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <cstdint>
+#include <optional>
+#include <string>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -47,6 +48,8 @@
 
 namespace compositionengine::impl {
 
+// Note that fields that affect HW composer state may need to be mirrored into
+// android::compositionengine::impl::planner::LayerState
 struct OutputLayerCompositionState {
     // The portion of the layer that is not obscured by opaque layers on top
     Region visibleRegion;
@@ -90,6 +93,9 @@
         sp<Fence> acquireFence = nullptr;
         Rect displayFrame = {};
         ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+        ProjectionSpace displaySpace;
+        Region damageRegion = Region::INVALID_REGION;
+        Region visibleRegion;
     } overrideInfo;
 
     /*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index a56f28a..afa02cd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <compositionengine/Output.h>
+#include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <renderengine/RenderEngine.h>
 
@@ -38,7 +40,10 @@
         const LayerState* getState() const { return mState; }
         const std::string& getName() const { return mState->getName(); }
         Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
-        const sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+        const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
+        const sp<GraphicBuffer>& getBuffer() const {
+            return mState->getOutputLayer()->getLayerFE().getCompositionState()->buffer;
+        }
         int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
         NonBufferHash getHash() const { return mHash; }
         std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
@@ -59,9 +64,11 @@
     size_t getLayerCount() const { return mLayers.size(); }
     const Layer& getFirstLayer() const { return mLayers[0]; }
     const Rect& getBounds() const { return mBounds; }
+    const Region& getVisibleRegion() const { return mVisibleRegion; }
     size_t getAge() const { return mAge; }
     const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
+    const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
     ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
 
     NonBufferHash getNonBufferHash() const;
@@ -89,11 +96,12 @@
         boundingRegion.orSelf(mBounds);
         boundingRegion.orSelf(other.mBounds);
         mBounds = boundingRegion.getBounds();
+        mVisibleRegion.orSelf(other.mVisibleRegion);
     }
     void incrementAge() { ++mAge; }
 
-    // Renders the cached set with the supplied output dataspace.
-    void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+    // Renders the cached set with the supplied output composition state.
+    void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
 
     void dump(std::string& result) const;
 
@@ -104,6 +112,7 @@
     std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
     std::vector<Layer> mLayers;
     Rect mBounds = Rect::EMPTY_RECT;
+    Region mVisibleRegion;
     size_t mAge = 0;
 
     class Texture {
@@ -132,7 +141,9 @@
 
     Texture mTexture;
     sp<Fence> mDrawFence;
+    ProjectionSpace mOutputSpace;
     ui::Dataspace mOutputDataspace;
+    ui::Transform::RotationFlags mOrientation = ui::Transform::ROT_0;
 
     static const bool sDebugHighlighLayers;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 2bd3249..313a180 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <compositionengine/Output.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
@@ -42,8 +43,9 @@
     NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                 std::chrono::steady_clock::time_point now);
 
-    // Renders the newest cached sets with the supplied output dataspace
-    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+    // Renders the newest cached sets with the supplied output composition state
+    void renderCachedSets(renderengine::RenderEngine& re,
+                          const OutputCompositionState& outputState);
 
     void dump(std::string& result) const;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index a3beadc..050e264 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -32,6 +32,14 @@
 struct hash<android::sp<T>> {
     size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
 };
+
+template <typename T>
+struct hash<android::wp<T>> {
+    size_t operator()(const android::wp<T>& p) {
+        android::sp<T> promoted = p.promote();
+        return std::hash<void*>()(promoted ? promoted.get() : nullptr);
+    }
+};
 } // namespace std
 
 namespace android::compositionengine::impl::planner {
@@ -49,13 +57,16 @@
     BufferTransform = 1u << 5,
     BlendMode       = 1u << 6,
     Alpha           = 1u << 7,
-    VisibleRegion   = 1u << 8,
-    Dataspace       = 1u << 9,
-    ColorTransform  = 1u << 10,
-    CompositionType = 1u << 11,
-    SidebandStream  = 1u << 12,
-    Buffer          = 1u << 13,
-    SolidColor      = 1u << 14,
+    LayerMetadata   = 1u << 8,
+    VisibleRegion   = 1u << 9,
+    Dataspace       = 1u << 10,
+    PixelFormat     = 1u << 11,
+    ColorTransform  = 1u << 12,
+    SurfaceDamage   = 1u << 13,
+    CompositionType = 1u << 14,
+    SidebandStream  = 1u << 15,
+    Buffer          = 1u << 16,
+    SolidColor      = 1u << 17,
 };
 // clang-format on
 
@@ -70,7 +81,7 @@
 
     virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
 
-    virtual size_t getHash(Flags<LayerStateField> skipFields) const = 0;
+    virtual size_t getHash() const = 0;
 
     virtual LayerStateField getField() const = 0;
 
@@ -87,6 +98,7 @@
     using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
     using ToStrings = std::function<std::vector<std::string>(const T&)>;
     using Equals = std::function<bool(const T&, const T&)>;
+    using Hashes = std::function<size_t(const T&)>;
 
     static ToStrings getDefaultToStrings() {
         return [](const T& value) {
@@ -99,20 +111,48 @@
         return [](const T& value) { return std::vector<std::string>{toString(value)}; };
     }
 
+    static ToStrings getRegionToStrings() {
+        return [](const Region& region) {
+            using namespace std::string_literals;
+            std::string dump;
+            region.dump(dump, "");
+            std::vector<std::string> split = base::Split(dump, "\n"s);
+            split.erase(split.begin()); // Strip the header
+            split.pop_back();           // Strip the last (empty) line
+            for (std::string& line : split) {
+                line.erase(0, 4); // Strip leading padding before each rect
+            }
+            return split;
+        };
+    }
+
     static Equals getDefaultEquals() {
         return [](const T& lhs, const T& rhs) { return lhs == rhs; };
     }
 
+    static Equals getRegionEquals() {
+        return [](const Region& lhs, const Region& rhs) { return lhs.hasSameRects(rhs); };
+    }
+
+    static Hashes getDefaultHashes() {
+        return [](const T& value) { return std::hash<T>{}(value); };
+    }
+
     OutputLayerState(ReadFromLayerState reader,
                      ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
-                     Equals equals = OutputLayerState::getDefaultEquals())
-          : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+                     Equals equals = OutputLayerState::getDefaultEquals(),
+                     Hashes hashes = OutputLayerState::getDefaultHashes())
+          : mReader(reader), mToStrings(toStrings), mEquals(equals), mHashes(hashes) {}
 
     ~OutputLayerState() override = default;
 
     // Returns this member's field flag if it was changed
     Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
         T newValue = mReader(layer);
+        return update(newValue);
+    }
+
+    Flags<LayerStateField> update(const T& newValue) {
         if (!mEquals(mValue, newValue)) {
             mValue = newValue;
             mHash = {};
@@ -124,12 +164,9 @@
     LayerStateField getField() const override { return FIELD; }
     const T& get() const { return mValue; }
 
-    size_t getHash(Flags<LayerStateField> skipFields) const override {
-        if (skipFields.test(FIELD)) {
-            return 0;
-        }
+    size_t getHash() const override {
         if (!mHash) {
-            mHash = std::hash<T>{}(mValue);
+            mHash = mHashes(mValue);
         }
         return *mHash;
     }
@@ -163,6 +200,7 @@
     const ReadFromLayerState mReader;
     const ToStrings mToStrings;
     const Equals mEquals;
+    const Hashes mHashes;
     T mValue = {};
     mutable std::optional<size_t> mHash = {};
 };
@@ -175,22 +213,22 @@
     Flags<LayerStateField> update(compositionengine::OutputLayer*);
 
     // Computes a hash for this LayerState.
-    // The hash is only computed from NonUniqueFields.
-    size_t getHash(Flags<LayerStateField> skipFields) const;
+    // The hash is only computed from NonUniqueFields, and excludes GraphicBuffers since they are
+    // not guaranteed to live longer than the LayerState object.
+    size_t getHash() const;
 
     // Returns the bit-set of differing fields between this LayerState and another LayerState.
-    // This bit-set is based on NonUniqueFields only
-    Flags<LayerStateField> getDifferingFields(const LayerState& other,
-                                              Flags<LayerStateField> skipFields) const;
+    // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
+    Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
 
     compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
     int32_t getId() const { return mId.get(); }
     const std::string& getName() const { return mName.get(); }
     Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+    const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
     hardware::graphics::composer::hal::Composition getCompositionType() const {
         return mCompositionType.get();
     }
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer.get(); }
 
     void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
     void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
@@ -255,39 +293,80 @@
     OutputLayerState<float, LayerStateField::Alpha> mAlpha{
             [](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
 
-    // TODO(b/180638831): Generic layer metadata
+    using LayerMetadataState =
+            OutputLayerState<GenericLayerMetadataMap, LayerStateField::LayerMetadata>;
+    LayerMetadataState
+            mLayerMetadata{[](auto layer) {
+                               return layer->getLayerFE().getCompositionState()->metadata;
+                           },
+                           [](const GenericLayerMetadataMap& metadata) {
+                               std::vector<std::string> result;
+                               if (metadata.empty()) {
+                                   result.push_back("{}");
+                                   return result;
+                               }
+                               result.push_back("{");
+                               for (const auto& [key, value] : metadata) {
+                                   std::string keyValueDump;
+                                   keyValueDump.append("           ");
+                                   keyValueDump.append(key);
+                                   keyValueDump.append("=");
+                                   keyValueDump.append(value.dumpAsString());
+                                   result.push_back(keyValueDump);
+                               }
+                               result.push_back("}");
+                               return result;
+                           },
+                           LayerMetadataState::getDefaultEquals(),
+                           [](const GenericLayerMetadataMap& metadata) {
+                               size_t hash = 0;
+                               for (const auto& [key, value] : metadata) {
+                                   size_t entryHash = 0;
+                                   hashCombineSingleHashed(entryHash,
+                                                           std::hash<std::string>{}(key));
+                                   hashCombineSingleHashed(entryHash,
+                                                           GenericLayerMetadataEntry::Hasher{}(
+                                                                   value));
+                                   hash ^= entryHash;
+                               }
+                               return hash;
+                           }};
 
     // Output-dependent per-frame state
 
-    OutputLayerState<Region, LayerStateField::VisibleRegion>
-            mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
-                           [](const Region& region) {
-                               using namespace std::string_literals;
-                               std::string dump;
-                               region.dump(dump, "");
-                               std::vector<std::string> split = base::Split(dump, "\n"s);
-                               split.erase(split.begin()); // Strip the header
-                               split.pop_back();           // Strip the last (empty) line
-                               for (std::string& line : split) {
-                                   line.erase(0, 4); // Strip leading padding before each rect
-                               }
-                               return split;
-                           },
-                           [](const Region& lhs, const Region& rhs) {
-                               return lhs.hasSameRects(rhs);
-                           }};
+    using VisibleRegionState = OutputLayerState<Region, LayerStateField::VisibleRegion>;
+    VisibleRegionState mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+                                      VisibleRegionState::getRegionToStrings(),
+                                      VisibleRegionState::getRegionEquals()};
 
     using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
     DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
                                     DataspaceState::getHalToStrings()};
 
-    // TODO(b/180638831): Buffer format
-
     // Output-independent per-frame state
 
+    using PixelFormatState = OutputLayerState<hardware::graphics::composer::hal::PixelFormat,
+                                              LayerStateField::PixelFormat>;
+    PixelFormatState
+            mPixelFormat{[](auto layer) {
+                             return layer->getLayerFE().getCompositionState()->buffer
+                                     ? static_cast<hardware::graphics::composer::hal::PixelFormat>(
+                                               layer->getLayerFE()
+                                                       .getCompositionState()
+                                                       ->buffer->getPixelFormat())
+                                     : hardware::graphics::composer::hal::PixelFormat::RGBA_8888;
+                         },
+                         PixelFormatState::getHalToStrings()};
+
     OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
-    // TODO(b/180638831): Surface damage
+    using SurfaceDamageState = OutputLayerState<Region, LayerStateField::SurfaceDamage>;
+    SurfaceDamageState
+            mSurfaceDamage{[](auto layer) {
+                               return layer->getLayerFE().getCompositionState()->surfaceDamage;
+                           },
+                           SurfaceDamageState::getRegionToStrings(),
+                           SurfaceDamageState::getRegionEquals()};
 
     using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
                                                   LayerStateField::CompositionType>;
@@ -311,10 +390,14 @@
                                 return std::vector<std::string>{base::StringPrintf("%p", p)};
                             }};
 
-    OutputLayerState<sp<GraphicBuffer>, LayerStateField::Buffer>
+    OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
             mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
-                    [](const sp<GraphicBuffer>& buffer) {
-                        return std::vector<std::string>{base::StringPrintf("%p", buffer.get())};
+                    [](const wp<GraphicBuffer>& buffer) {
+                        sp<GraphicBuffer> promotedBuffer = buffer.promote();
+                        return std::vector<std::string>{
+                                base::StringPrintf("%p",
+                                                   promotedBuffer ? promotedBuffer.get()
+                                                                  : nullptr)};
                     }};
 
     int64_t mFramesSinceBufferUpdate = 0;
@@ -327,10 +410,12 @@
                             return std::vector<std::string>{stream.str()};
                         }};
 
-    std::array<StateInterface*, 13> getNonUniqueFields() {
-        std::array<const StateInterface*, 13> constFields =
+    static const constexpr size_t kNumNonUniqueFields = 16;
+
+    std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
+        std::array<const StateInterface*, kNumNonUniqueFields> constFields =
                 const_cast<const LayerState*>(this)->getNonUniqueFields();
-        std::array<StateInterface*, 13> fields;
+        std::array<StateInterface*, kNumNonUniqueFields> fields;
         std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
                        [](const StateInterface* constField) {
                            return const_cast<StateInterface*>(constField);
@@ -338,12 +423,12 @@
         return fields;
     }
 
-    std::array<const StateInterface*, 13> getNonUniqueFields() const {
+    std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
         return {
-                &mDisplayFrame,   &mSourceCrop,      &mZOrder,         &mBufferTransform,
-                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mOutputDataspace,
-                &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
-                &mSolidColor,
+                &mDisplayFrame,    &mSourceCrop,     &mZOrder,         &mBufferTransform,
+                &mBlendMode,       &mAlpha,          &mLayerMetadata,  &mVisibleRegion,
+                &mOutputDataspace, &mPixelFormat,    &mColorTransform, &mSurfaceDamage,
+                &mCompositionType, &mSidebandStream, &mBuffer,         &mSolidColor,
         };
     }
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 89de34d..e6d2b63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,8 @@
             compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
 
     // The planner will call to the Flattener to render any pending cached set
-    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+    void renderCachedSets(renderengine::RenderEngine& re,
+                          const OutputCompositionState& outputState);
 
     void dump(const Vector<String16>& args, std::string&);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ded2dcc..aed3be9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1252,7 +1252,7 @@
 
 void Output::renderCachedSets() {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
+        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b364649..3f36a8f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -354,7 +354,8 @@
 
     Rect displayFrame = outputDependentState.displayFrame;
     FloatRect sourceCrop = outputDependentState.sourceCrop;
-    if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+
+    if (outputDependentState.overrideInfo.buffer != nullptr) {
         displayFrame = outputDependentState.overrideInfo.displayFrame;
         sourceCrop = displayFrame.toFloatRect();
     }
@@ -380,8 +381,9 @@
               outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    // Solid-color layers should always use an identity transform.
-    const auto bufferTransform = requestedCompositionType != hal::Composition::SOLID_COLOR
+    // Solid-color layers and overridden buffers should always use an identity transform.
+    const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+                                  getState().overrideInfo.buffer == nullptr)
             ? outputDependentState.bufferTransform
             : static_cast<hal::Transform>(0);
     if (auto error = hwcLayer->setTransform(static_cast<hal::Transform>(bufferTransform));
@@ -395,14 +397,17 @@
 void OutputLayer::writeOutputIndependentGeometryStateToHWC(
         HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
         bool skipLayer) {
-    if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
-        error != hal::Error::NONE) {
+    const auto blendMode = getState().overrideInfo.buffer
+            ? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
+            : outputIndependentState.blendMode;
+    if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
-              toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
+              toString(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+    const float alpha = skipLayer
+            ? 0.0f
+            : (getState().overrideInfo.buffer ? 1.0f : outputIndependentState.alpha);
     ALOGV("Writing alpha %f", alpha);
 
     if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
@@ -424,18 +429,22 @@
 
     // TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
     // state and should not change every frame.
-    if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
-        error != hal::Error::NONE) {
+    Region visibleRegion = outputDependentState.overrideInfo.buffer
+            ? Region(outputDependentState.overrideInfo.visibleRegion)
+            : outputDependentState.outputSpaceVisibleRegion;
+    if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
               to_string(error).c_str(), static_cast<int32_t>(error));
         outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
     }
 
-    if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
-        error != hal::Error::NONE) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
-              outputDependentState.dataspace, to_string(error).c_str(),
-              static_cast<int32_t>(error));
+    const auto dataspace = outputDependentState.overrideInfo.buffer
+            ? outputDependentState.overrideInfo.dataspace
+            : outputDependentState.dataspace;
+
+    if (auto error = hwcLayer->setDataspace(dataspace); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
+              to_string(error).c_str(), static_cast<int32_t>(error));
     }
 }
 
@@ -452,8 +461,11 @@
                   to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
-        error != hal::Error::NONE) {
+    const Region& surfaceDamage = getState().overrideInfo.buffer
+            ? getState().overrideInfo.damageRegion
+            : outputIndependentState.surfaceDamage;
+
+    if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
               to_string(error).c_str(), static_cast<int32_t>(error));
         outputIndependentState.surfaceDamage.dump(LOG_TAG);
@@ -671,16 +683,26 @@
         return {};
     }
 
+    // Compute the geometry boundaries in layer stack space: we need to transform from the
+    // framebuffer space of the override buffer to layer space.
+    const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
+    const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
+    const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+
     LayerFE::LayerSettings settings;
     settings.geometry = renderengine::Geometry{
-            .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+            .boundaries = boundaries.toFloatRect(),
     };
     settings.bufferId = getState().overrideInfo.buffer->getId();
-    settings.source =
-            renderengine::PixelSource{.buffer = renderengine::Buffer{
-                                              .buffer = getState().overrideInfo.buffer,
-                                              .fence = getState().overrideInfo.acquireFence,
-                                      }};
+    settings.source = renderengine::PixelSource{
+            .buffer = renderengine::Buffer{
+                    .buffer = getState().overrideInfo.buffer,
+                    .fence = getState().overrideInfo.acquireFence,
+                    // If the transform from layer space to display space contains a rotation, we
+                    // need to undo the rotation in the texture transform
+                    .textureTransform =
+                            ui::Transform(transform.inverse().getOrientation(), 1, 1).asMatrix4(),
+            }};
     settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 45dce98..efd23dc 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,13 @@
     dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
     dumpVal(out, "override display frame", overrideInfo.displayFrame);
     dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
+    dumpVal(out, "override display space", to_string(overrideInfo.displaySpace));
+    std::string damageRegionString;
+    overrideInfo.damageRegion.dump(damageRegionString, "");
+    dumpVal(out, "override damage region", damageRegionString);
+    std::string visibleRegionString;
+    overrideInfo.visibleRegion.dump(visibleRegionString, "");
+    dumpVal(out, "override visible region", visibleRegionString);
 
     if (hwc) {
         dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 4d3036a..dcb7555 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -19,6 +19,7 @@
 // #define LOG_NDEBUG 0
 
 #include <android-base/properties.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <math/HashCombine.h>
 #include <renderengine/DisplaySettings.h>
@@ -50,17 +51,18 @@
 }
 
 CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
-      : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+      : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
 
 CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
-      : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+      : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
     addLayer(layer, lastUpdate);
 }
 
 CachedSet::CachedSet(Layer layer)
       : mFingerprint(layer.getHash()),
         mLastUpdate(layer.getLastUpdate()),
-        mBounds(layer.getDisplayFrame()) {
+        mBounds(layer.getDisplayFrame()),
+        mVisibleRegion(layer.getVisibleRegion()) {
     mLayers.emplace_back(std::move(layer));
 }
 
@@ -72,6 +74,7 @@
     boundingRegion.orSelf(mBounds);
     boundingRegion.orSelf(layer->getDisplayFrame());
     mBounds = boundingRegion.getBounds();
+    mVisibleRegion.orSelf(layer->getVisibleRegion());
 }
 
 NonBufferHash CachedSet::getNonBufferHash() const {
@@ -84,6 +87,7 @@
     size_t hash = 0;
     android::hashCombineSingle(hash, mBounds);
     android::hashCombineSingle(hash, mOutputDataspace);
+    android::hashCombineSingle(hash, mOrientation);
     return hash;
 }
 
@@ -148,17 +152,22 @@
     }
 }
 
-void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine,
+                       const OutputCompositionState& outputState) {
+    const Rect& viewport = outputState.layerStackSpace.content;
+    const ui::Dataspace& outputDataspace = outputState.dataspace;
+    const ui::Transform::RotationFlags orientation =
+            ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
     renderengine::DisplaySettings displaySettings{
             .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
-            .clip = mBounds,
+            .clip = viewport,
             .outputDataspace = outputDataspace,
+            .orientation = orientation,
     };
 
     Region clearRegion = Region::INVALID_REGION;
-    Rect viewport = mBounds;
     LayerFE::ClientCompositionTargetSettings targetSettings{
-            .clip = Region(mBounds),
+            .clip = Region(viewport),
             .needsFiltering = false,
             .isSecure = true,
             .supportsProtectedContent = false,
@@ -171,6 +180,7 @@
     };
 
     std::vector<renderengine::LayerSettings> layerSettings;
+    renderengine::LayerSettings highlight;
     for (const auto& layer : mLayers) {
         const auto clientCompositionList =
                 layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
@@ -185,7 +195,7 @@
                    [](const renderengine::LayerSettings& settings) { return &settings; });
 
     if (sDebugHighlighLayers) {
-        renderengine::LayerSettings highlight{
+        highlight = {
                 .geometry =
                         renderengine::Geometry{
                                 .boundaries = FloatRect(0.0f, 0.0f,
@@ -216,7 +226,12 @@
     if (result == NO_ERROR) {
         mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
+        mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
+                                                outputState.framebufferSpace.bounds.getHeight()),
+                                       mBounds);
+        mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
+        mOrientation = orientation;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 60ebbb2..ffca5ba 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -52,12 +52,12 @@
 }
 
 void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
-                                 ui::Dataspace outputDataspace) {
+                                 const OutputCompositionState& outputState) {
     if (!mNewCachedSet) {
         return;
     }
 
-    mNewCachedSet->render(renderEngine, outputDataspace);
+    mNewCachedSet->render(renderEngine, outputState);
 }
 
 void Flattener::dump(std::string& result) const {
@@ -193,9 +193,7 @@
     auto currentLayerIter = mLayers.begin();
     auto incomingLayerIter = layers.begin();
     while (incomingLayerIter != layers.end()) {
-        if (mNewCachedSet &&
-            mNewCachedSet->getFingerprint() ==
-                    (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
+        if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
             if (mNewCachedSet->hasBufferUpdate()) {
                 ALOGV("[%s] Dropping new cached set", __func__);
                 ++mInvalidatedCachedSetAges[0];
@@ -213,6 +211,9 @@
                                 .acquireFence = mNewCachedSet->getDrawFence(),
                                 .displayFrame = mNewCachedSet->getBounds(),
                                 .dataspace = mNewCachedSet->getOutputDataspace(),
+                                .displaySpace = mNewCachedSet->getOutputSpace(),
+                                .damageRegion = Region::INVALID_REGION,
+                                .visibleRegion = mNewCachedSet->getVisibleRegion(),
                         };
                         ++incomingLayerIter;
                     }
@@ -244,6 +245,9 @@
                         .acquireFence = currentLayerIter->getDrawFence(),
                         .displayFrame = currentLayerIter->getBounds(),
                         .dataspace = currentLayerIter->getOutputDataspace(),
+                        .displaySpace = currentLayerIter->getOutputSpace(),
+                        .damageRegion = Region(),
+                        .visibleRegion = currentLayerIter->getVisibleRegion(),
                 };
                 ++incomingLayerIter;
             }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 222b2be..7c32e94 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -58,24 +58,24 @@
     return differences;
 }
 
-size_t LayerState::getHash(
-        Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+size_t LayerState::getHash() const {
     size_t hash = 0;
     for (const StateInterface* field : getNonUniqueFields()) {
-        android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+        if (field->getField() == LayerStateField::Buffer) {
+            continue;
+        }
+        android::hashCombineSingleHashed(hash, field->getHash());
     }
 
     return hash;
 }
 
-Flags<LayerStateField> LayerState::getDifferingFields(
-        const LayerState& other,
-        Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
     Flags<LayerStateField> differences;
     auto myFields = getNonUniqueFields();
     auto otherFields = other.getNonUniqueFields();
     for (size_t i = 0; i < myFields.size(); ++i) {
-        if (skipFields.test(myFields[i]->getField())) {
+        if (myFields[i]->getField() == LayerStateField::Buffer) {
             continue;
         }
 
@@ -157,9 +157,11 @@
     return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
             lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
             lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
-            lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
-            lhs.mOutputDataspace == rhs.mOutputDataspace &&
+            lhs.mAlpha == rhs.mAlpha && lhs.mLayerMetadata == rhs.mLayerMetadata &&
+            lhs.mVisibleRegion == rhs.mVisibleRegion &&
+            lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat &&
             lhs.mColorTransform == rhs.mColorTransform &&
+            lhs.mSurfaceDamage == rhs.mSurfaceDamage &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
             (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
@@ -169,7 +171,7 @@
 NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
     size_t hash = 0;
     for (const auto layer : layers) {
-        android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+        android::hashCombineSingleHashed(hash, layer->getHash());
     }
 
     return hash;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 87721c7..ad75557 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -134,8 +134,8 @@
 }
 
 void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
-                               ui::Dataspace outputDataspace) {
-    mFlattener.renderCachedSets(renderEngine, outputDataspace);
+                               const OutputCompositionState& outputState) {
+    mFlattener.renderCachedSets(renderEngine, outputState);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 07920b8..8226ef7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -33,8 +33,7 @@
     std::optional<ApproximateMatch> approximateMatch = {};
     for (size_t i = 0; i < mLayers.size(); ++i) {
         // Skip identical layers
-        if (mLayers[i].getHash(LayerStateField::Buffer) ==
-            other[i]->getHash(LayerStateField::Buffer)) {
+        if (mLayers[i].getHash() == other[i]->getHash()) {
             continue;
         }
 
@@ -56,8 +55,7 @@
             return std::nullopt;
         }
 
-        Flags<LayerStateField> differingFields =
-                mLayers[i].getDifferingFields(*other[i], LayerStateField::Buffer);
+        Flags<LayerStateField> differingFields = mLayers[i].getDifferingFields(*other[i]);
 
         // If we don't find an approximate match on this layer, then the LayerStacks differ
         // by too much, so return nothing
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9dd199d..8a4d161 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -690,10 +690,15 @@
     static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
     static constexpr uint32_t kZOrder = 21u;
     static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+    static constexpr Hwc2::Transform kOverrideBufferTransform = static_cast<Hwc2::Transform>(0);
     static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
             static_cast<Hwc2::IComposerClient::BlendMode>(41);
+    static constexpr Hwc2::IComposerClient::BlendMode kOverrideBlendMode =
+            Hwc2::IComposerClient::BlendMode::PREMULTIPLIED;
     static constexpr float kAlpha = 51.f;
+    static constexpr float kOverrideAlpha = 1.f;
     static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+    static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
@@ -701,13 +706,18 @@
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
+    static const Rect kOverrideDisplayFrame;
     static const Region kOutputSpaceVisibleRegion;
+    static const Region kOverrideVisibleRegion;
     static const mat4 kColorTransform;
     static const Region kSurfaceDamage;
+    static const Region kOverrideSurfaceDamage;
     static const HdrMetadata kHdrMetadata;
     static native_handle_t* kSidebandStreamHandle;
     static const sp<GraphicBuffer> kBuffer;
+    static const sp<GraphicBuffer> kOverrideBuffer;
     static const sp<Fence> kFence;
+    static const sp<Fence> kOverrideFence;
     static const std::string kLayerGenericMetadata1Key;
     static const std::vector<uint8_t> kLayerGenericMetadata1Value;
     static const std::string kLayerGenericMetadata2Key;
@@ -751,26 +761,42 @@
                                                              kLayerGenericMetadata2Value};
     }
 
-    void expectGeometryCommonCalls() {
-        EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
+    void includeOverrideInfo() {
+        auto& overrideInfo = mOutputLayer.editState().overrideInfo;
 
-        EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+        overrideInfo.buffer = kOverrideBuffer;
+        overrideInfo.acquireFence = kOverrideFence;
+        overrideInfo.displayFrame = kOverrideDisplayFrame;
+        overrideInfo.dataspace = kOverrideDataspace;
+        overrideInfo.damageRegion = kOverrideSurfaceDamage;
+        overrideInfo.visibleRegion = kOverrideVisibleRegion;
     }
 
-    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
-        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
-                .WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+    void expectGeometryCommonCalls(Rect displayFrame = kDisplayFrame,
+                                   FloatRect sourceCrop = kSourceCrop,
+                                   Hwc2::Transform bufferTransform = kBufferTransform,
+                                   Hwc2::IComposerClient::BlendMode blendMode = kBlendMode,
+                                   float alpha = kAlpha) {
+        EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
+
+        EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setPlaneAlpha(alpha)).WillOnce(Return(kError));
+    }
+
+    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
+                                   ui::Dataspace dataspace = kDataspace,
+                                   const Region& visibleRegion = kOutputSpaceVisibleRegion,
+                                   const Region& surfaceDamage = kSurfaceDamage) {
+        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
                 .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
                                          ? hal::Error::UNSUPPORTED
                                          : hal::Error::NONE));
-        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
-                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
     }
 
     void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
@@ -793,9 +819,10 @@
         EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
     }
 
-    void expectSetHdrMetadataAndBufferCalls() {
+    void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+                                            sp<Fence> fence = kFence) {
         EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
-        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
     }
 
     void expectGenericLayerMetadataCalls() {
@@ -817,18 +844,23 @@
 const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
                                                    84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
 const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
         Rect{1005, 1006, 1007, 1008}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
 const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
         1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
         1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
 };
 const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideSurfaceDamage{Rect{1026, 1027, 1028, 1029}};
 const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
 native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
         reinterpret_cast<native_handle_t*>(1031);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = new GraphicBuffer();
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
         "com.example.metadata.1";
 const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -983,6 +1015,20 @@
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeOverrideInfo();
+
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
+                              kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+                              kOverrideSurfaceDamage);
+    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+}
+
 /*
  * OutputLayer::writeCursorPositionToHWC()
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 7842efb..f01fe27 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <compositionengine/mock/LayerFE.h>
@@ -59,6 +60,7 @@
 
     static constexpr size_t kNumLayers = 5;
     std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+    impl::OutputCompositionState mOutputState;
 
     android::renderengine::mock::RenderEngine mRenderEngine;
 };
@@ -68,6 +70,8 @@
         auto testLayer = std::make_unique<TestLayer>();
         auto pos = static_cast<int32_t>(i);
         testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+        testLayer->outputLayerCompositionState.visibleRegion =
+                Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
 
         testLayer->layerFE = sp<mock::LayerFE>::make();
 
@@ -87,6 +91,12 @@
                 std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
 
         mTestLayers.emplace_back(std::move(testLayer));
+
+        // set up minimium params needed for rendering
+        mOutputState.dataspace = ui::Dataspace::SRGB;
+        mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
     }
 }
 
@@ -98,6 +108,7 @@
     EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
     EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
     EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+    EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
     EXPECT_EQ(1u, cachedSet.getLayerCount());
     EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
     EXPECT_EQ(0u, cachedSet.getAge());
@@ -146,6 +157,10 @@
     EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    EXPECT_TRUE(cachedSet.getVisibleRegion().hasSameRects(expectedRegion));
     EXPECT_EQ(2u, cachedSet.getLayerCount());
     EXPECT_EQ(0u, cachedSet.getAge());
     expectNoBuffer(cachedSet);
@@ -231,6 +246,11 @@
     EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    expectedRegion.orSelf(Rect(3, 3, 4, 4));
+    EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
     EXPECT_EQ(3u, cachedSet1.getLayerCount());
     EXPECT_EQ(0u, cachedSet1.getAge());
     expectNoBuffer(cachedSet1);
@@ -288,7 +308,9 @@
                                 const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                                 base::unique_fd*) -> size_t {
         EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
-        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
+        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+                  displaySettings.orientation);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
@@ -300,9 +322,14 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
     EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
-    cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+    cachedSet.render(mRenderEngine, mOutputState);
     expectReadyBuffer(cachedSet);
 
+    EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
+    EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+                   mOutputState.framebufferSpace.bounds.getHeight()),
+              cachedSet.getOutputSpace().bounds);
+
     // Now check that appending a new cached set properly cleans up RenderEngine resources.
     EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
     CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index bd77559..c528087 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
@@ -76,6 +77,7 @@
 
     static constexpr size_t kNumLayers = 5;
     std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+    impl::OutputCompositionState mOutputState;
 };
 
 void FlattenerTest::SetUp() {
@@ -87,6 +89,8 @@
         testLayer->name = ss.str();
 
         testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+        testLayer->outputLayerCompositionState.visibleRegion =
+                Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
 
         testLayer->layerFECompositionState.buffer =
                 new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -120,6 +124,11 @@
         testLayer->layerState->incrementFramesSinceBufferUpdate();
 
         mTestLayers.emplace_back(std::move(testLayer));
+
+        // set up minimium params needed for rendering
+        mOutputState.dataspace = ui::Dataspace::SRGB;
+        mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
     }
 }
 
@@ -134,13 +143,13 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 }
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -150,7 +159,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -160,7 +169,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -195,7 +204,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 }
 
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -241,13 +250,87 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
     EXPECT_EQ(overrideBuffer2, overrideBuffer3);
 }
 
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideDisplaySpace =
+            layerState1->getOutputLayer()->getState().overrideInfo.displaySpace;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+
+    EXPECT_EQ(overrideDisplaySpace.bounds,
+              Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+                   mOutputState.framebufferSpace.bounds.getHeight()));
+    EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
+    EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideDamageRegion =
+            layerState1->getOutputLayer()->getState().overrideInfo.damageRegion;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+    EXPECT_TRUE(overrideDamageRegion.isRect() &&
+                overrideDamageRegion.bounds() == Rect::INVALID_RECT);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    EXPECT_TRUE(overrideDamageRegion.isRect() && overrideDamageRegion.bounds() == Rect::EMPTY_RECT);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsVisibleRegion) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideVisibleRegion =
+            layerState1->getOutputLayer()->getState().overrideInfo.visibleRegion;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    EXPECT_TRUE(overrideVisibleRegion.hasSameRects(expectedRegion));
+}
+
 TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
     auto& layerState1 = mTestLayers[0]->layerState;
     const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
@@ -276,7 +359,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -313,7 +396,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -322,7 +405,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -335,7 +418,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -344,7 +427,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -386,7 +469,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -399,7 +482,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -411,7 +495,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -426,7 +511,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -437,7 +522,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 8f235ab..47b1ae8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -24,6 +24,9 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include "android/hardware_buffer.h"
+#include "compositionengine/LayerFECompositionState.h"
+
 namespace android::compositionengine::impl::planner {
 namespace {
 
@@ -48,7 +51,15 @@
 native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
 native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
 const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
-const half4 sHalf4Two = half4(0.5f, 0.4f, 0.43, 0.2f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.3f, 0.2f);
+const std::string sMetadataKeyOne = std::string("Meta!");
+const std::string sMetadataKeyTwo = std::string("Data!");
+const GenericLayerMetadataEntry sMetadataValueOne = GenericLayerMetadataEntry{
+        .value = std::vector<uint8_t>({1, 2}),
+};
+const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
+        .value = std::vector<uint8_t>({1, 3}),
+};
 
 struct LayerStateTest : public testing::Test {
     LayerStateTest() {
@@ -75,6 +86,21 @@
         EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
     }
 
+    void verifyUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs) {
+        EXPECT_EQ(lhs.getHash(), rhs.getHash());
+
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), lhs.getDifferingFields(rhs));
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), rhs.getDifferingFields(lhs));
+    }
+
+    void verifyNonUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs,
+                                        Flags<LayerStateField> fields) {
+        EXPECT_NE(lhs.getHash(), rhs.getHash());
+
+        EXPECT_EQ(fields, lhs.getDifferingFields(rhs));
+        EXPECT_EQ(fields, rhs.getDifferingFields(lhs));
+    }
+
     mock::LayerFE mLayerFE;
     mock::OutputLayer mOutputLayer;
     std::unique_ptr<LayerState> mLayerState;
@@ -129,21 +155,7 @@
     EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
 
     // Id is a unique field, so it's not computed in the hash for a layer state.
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
-              otherLayerState->getHash(LayerStateField::Id));
-
-    // Similarly, Id cannot be included in differing fields.
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
-
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
     EXPECT_FALSE(otherLayerState->compare(*mLayerState));
 }
@@ -188,21 +200,7 @@
     EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
 
     // Name is a unique field, so it's not computed in the hash for a layer state.
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
-              otherLayerState->getHash(LayerStateField::Name));
-
-    // Similarly, Name cannot be included in differing fields.
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
-
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
     EXPECT_FALSE(otherLayerState->compare(*mLayerState));
 }
@@ -253,20 +251,7 @@
 
     EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
-              otherLayerState->getHash(LayerStateField::DisplayFrame));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::DisplayFrame));
-
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::DisplayFrame);
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
@@ -337,34 +322,12 @@
 
     EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
-              otherLayerState->getHash(LayerStateField::CompositionType));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
-
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   LayerStateField::CompositionType);
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
-TEST_F(LayerStateTest, getBuffer) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
-}
-
 TEST_F(LayerStateTest, updateBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
@@ -380,7 +343,6 @@
     setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
-    EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
     EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
 }
 
@@ -399,21 +361,8 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
-
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
-              otherLayerState->getHash(LayerStateField::Buffer));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+    // A buffer is not a unique field, but the assertions are the same.
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
 
     // Buffers are explicitly excluded from comparison
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
@@ -453,19 +402,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
-              otherLayerState->getHash(LayerStateField::SourceCrop));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SourceCrop);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -504,19 +441,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
-              otherLayerState->getHash(LayerStateField::ZOrder));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ZOrder);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -555,19 +480,8 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
-              otherLayerState->getHash(LayerStateField::BufferTransform));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BufferTransform));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   LayerStateField::BufferTransform);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -606,19 +520,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
-              otherLayerState->getHash(LayerStateField::BlendMode));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BlendMode));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlendMode);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -657,24 +559,61 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
-              otherLayerState->getHash(LayerStateField::Alpha));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Alpha);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
+TEST_F(LayerStateTest, updateLayerMetadata) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::LayerMetadata), updates);
+}
+
+TEST_F(LayerStateTest, compareLayerMetadata) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::LayerMetadata);
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_TRUE(mLayerState->getVisibleRegion().hasSameRects(sRegionOne));
+}
+
 TEST_F(LayerStateTest, updateVisibleRegion) {
     OutputLayerCompositionState outputLayerCompositionState;
     outputLayerCompositionState.visibleRegion = sRegionOne;
@@ -708,19 +647,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
-              otherLayerState->getHash(LayerStateField::VisibleRegion));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::VisibleRegion));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::VisibleRegion);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -759,19 +686,65 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
-              otherLayerState->getHash(LayerStateField::Dataspace));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Dataspace);
 
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updatePixelFormat) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer1");
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer2");
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer) |
+                      Flags<LayerStateField>(LayerStateField::PixelFormat),
+              updates);
+}
+
+TEST_F(LayerStateTest, comparePixelFormat) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer1");
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer2");
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   Flags<LayerStateField>(LayerStateField::PixelFormat));
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -814,19 +787,48 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
-              otherLayerState->getHash(LayerStateField::ColorTransform));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ColorTransform);
 
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ColorTransform));
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSurfaceDamage) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.surfaceDamage = sRegionOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SurfaceDamage), updates);
+}
+
+TEST_F(LayerStateTest, compareSurfaceDamage) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.surfaceDamage = sRegionOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SurfaceDamage);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -865,19 +867,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
-              otherLayerState->getHash(LayerStateField::SidebandStream));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SidebandStream));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SidebandStream);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -916,19 +906,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
-              otherLayerState->getHash(LayerStateField::SolidColor));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SolidColor));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SolidColor);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index f7fc162..8d685cf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,14 +76,14 @@
     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
                                        GRALLOC_USAGE_HW_RENDER |
                                        GRALLOC_USAGE_HW_COMPOSER);
-    const auto limitedSize = limitFramebufferSize(size);
+    const auto limitedSize = limitSize(size);
     mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
     mConsumer->setMaxAcquiredBufferCount(
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
 void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
-    const auto limitedSize = limitFramebufferSize(newSize);
+    const auto limitedSize = limitSize(newSize);
     mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
 }
 
@@ -179,19 +179,23 @@
     }
 }
 
-ui::Size FramebufferSurface::limitFramebufferSize(const ui::Size& size) {
+ui::Size FramebufferSurface::limitSize(const ui::Size& size) {
+    return limitSizeInternal(size, mMaxSize);
+}
+
+ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::Size& maxSize) {
     ui::Size limitedSize = size;
     bool wasLimited = false;
-    if (size.width > mMaxSize.width && mMaxSize.width != 0) {
+    if (size.width > maxSize.width && maxSize.width != 0) {
         const float aspectRatio = static_cast<float>(size.width) / size.height;
-        limitedSize.height = mMaxSize.width / aspectRatio;
-        limitedSize.width = mMaxSize.width;
+        limitedSize.height = maxSize.width / aspectRatio;
+        limitedSize.width = maxSize.width;
         wasLimited = true;
     }
-    if (size.height > mMaxSize.height && mMaxSize.height != 0) {
+    if (limitedSize.height > maxSize.height && maxSize.height != 0) {
         const float aspectRatio = static_cast<float>(size.width) / size.height;
-        limitedSize.height = mMaxSize.height;
-        limitedSize.width = mMaxSize.height * aspectRatio;
+        limitedSize.height = maxSize.height;
+        limitedSize.width = maxSize.height * aspectRatio;
         wasLimited = true;
     }
     ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 5d1e131..3123351 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -55,15 +55,20 @@
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
 private:
+    friend class FramebufferSurfaceTest;
+
+    // Limits the width and height by the maximum width specified.
+    ui::Size limitSize(const ui::Size&);
+
+    // Used for testing purposes.
+    static ui::Size limitSizeInternal(const ui::Size&, const ui::Size& maxSize);
+
     virtual ~FramebufferSurface() { }; // this class cannot be overloaded
 
     virtual void freeBufferLocked(int slotIndex);
 
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
-    // Limits the width and height by the maximum width specified in the constructor.
-    ui::Size limitFramebufferSize(const ui::Size&);
-
     // nextBuffer waits for and then latches the next buffer from the
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 2784861..6485265 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -575,13 +575,9 @@
             }
         } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
             // Finish late, Present late
-            if (displayFrameJankType == JankType::None) {
-                // Display frame is not janky, so purely app's fault
-                mJankType |= JankType::AppDeadlineMissed;
-            } else {
-                // Propagate DisplayFrame's jankType if it is janky
-                mJankType |= displayFrameJankType;
-            }
+            mJankType |= JankType::AppDeadlineMissed;
+            // Propagate DisplayFrame's jankType if it is janky
+            mJankType |= displayFrameJankType;
         }
     }
 }
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 8ad805b..178c531 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -62,10 +62,9 @@
     mFrameRecords[mOffset].actualPresentTime = presentTime;
 }
 
-void FrameTracker::setActualPresentFence(
-        std::shared_ptr<FenceTime>&& readyFence) {
+void FrameTracker::setActualPresentFence(const std::shared_ptr<FenceTime>& readyFence) {
     Mutex::Autolock lock(mMutex);
-    mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
+    mFrameRecords[mOffset].actualPresentFence = readyFence;
     mNumFences++;
 }
 
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 35382be..bc412ae 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -66,7 +66,7 @@
 
     // setActualPresentFence sets the fence that is used to get the time
     // at which the current frame became visible to the user.
-    void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
+    void setActualPresentFence(const std::shared_ptr<FenceTime>& fence);
 
     // setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
     // This is used to compute frame presentation duration statistics relative
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
new file mode 100644
index 0000000..c06e300
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HdrLayerInfoReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <utils/Trace.h>
+
+#include "HdrLayerInfoReporter.h"
+
+namespace android {
+
+void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
+    ATRACE_CALL();
+    std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
+    {
+        std::scoped_lock lock(mMutex);
+        toInvoke.reserve(mListeners.size());
+        for (auto& [key, it] : mListeners) {
+            if (it.lastInfo != info) {
+                it.lastInfo = info;
+                toInvoke.push_back(it.listener);
+            }
+        }
+    }
+
+    for (const auto& listener : toInvoke) {
+        ATRACE_NAME("invoking onHdrLayerInfoChanged");
+        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+    }
+}
+
+void HdrLayerInfoReporter::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mMutex);
+    mListeners.erase(who);
+}
+
+void HdrLayerInfoReporter::addListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
+    asBinder->linkToDeath(this);
+    std::lock_guard lock(mMutex);
+    mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, HdrLayerInfo{}});
+}
+
+void HdrLayerInfoReporter::removeListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+    std::lock_guard lock(mMutex);
+    mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
new file mode 100644
index 0000000..671395f
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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 <android-base/thread_annotations.h>
+#include <android/gui/IHdrLayerInfoListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+namespace android {
+
+class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
+public:
+    struct HdrLayerInfo {
+        int32_t numberOfHdrLayers = 0;
+        int32_t maxW = 0;
+        int32_t maxH = 0;
+        int32_t flags = 0;
+
+        bool operator==(const HdrLayerInfo& other) const {
+            return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
+                    maxH == other.maxH && flags == other.flags;
+        }
+
+        bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+    };
+
+    HdrLayerInfoReporter() = default;
+    ~HdrLayerInfoReporter() final = default;
+
+    // Dispatches updated layer fps values for the registered listeners
+    // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+    // must be held when calling this method.
+    void dispatchHdrLayerInfo(const HdrLayerInfo& info) EXCLUDES(mMutex);
+
+    // Override for IBinder::DeathRecipient
+    void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
+
+    // Registers an Fps listener that listens to fps updates for the provided layer
+    void addListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+    // Deregisters an Fps listener
+    void removeListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+
+    bool hasListeners() const EXCLUDES(mMutex) {
+        std::scoped_lock lock(mMutex);
+        return !mListeners.empty();
+    }
+
+private:
+    mutable std::mutex mMutex;
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    struct TrackedListener {
+        sp<gui::IHdrLayerInfoListener> listener;
+        HdrLayerInfo lastInfo;
+    };
+
+    std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 447cbb3..2fcd821 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,7 +116,8 @@
     mCurrentState.bufferTransform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
-    mCurrentState.acquireFence = new Fence(-1);
+    mCurrentState.acquireFence = sp<Fence>::make(-1);
+    mCurrentState.acquireFenceTime = std::make_shared<FenceTime>(mCurrentState.acquireFence);
     mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
     mCurrentState.hdrMetadata.validTypes = 0;
     mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
@@ -2867,6 +2868,18 @@
     }
 }
 
+scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+    switch (strategy) {
+        case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+            return Seamlessness::OnlySeamless;
+        case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+            return Seamlessness::SeamedAndSeamless;
+        default:
+            LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+            return Seamlessness::Default;
+    }
+}
+
 bool Layer::getPrimaryDisplayOnly() const {
     const State& s(mDrawingState);
     if (s.flags & layer_state_t::eLayerSkipScreenshot) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4cd379b..4a105eb 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -52,6 +52,7 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "RenderArea.h"
+#include "Scheduler/LayerInfo.h"
 #include "Scheduler/Seamlessness.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceTracing.h"
@@ -141,60 +142,8 @@
         float radius = 0.0f;
     };
 
-    // FrameRateCompatibility specifies how we should interpret the frame rate associated with
-    // the layer.
-    enum class FrameRateCompatibility {
-        Default, // Layer didn't specify any specific handling strategy
-
-        Exact, // Layer needs the exact frame rate.
-
-        ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
-                         // content properly. Any other value will result in a pull down.
-
-        NoVote, // Layer doesn't have any requirements for the refresh rate and
-                // should not be considered when the display refresh rate is determined.
-    };
-
-    // Encapsulates the frame rate and compatibility of the layer. This information will be used
-    // when the display refresh rate is determined.
-    struct FrameRate {
-        using Seamlessness = scheduler::Seamlessness;
-
-        Fps rate;
-        FrameRateCompatibility type;
-        Seamlessness seamlessness;
-
-        FrameRate()
-              : rate(0),
-                type(FrameRateCompatibility::Default),
-                seamlessness(Seamlessness::Default) {}
-        FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
-              : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {}
-
-        bool operator==(const FrameRate& other) const {
-            return rate.equalsWithMargin(other.rate) && type == other.type &&
-                    seamlessness == other.seamlessness;
-        }
-
-        bool operator!=(const FrameRate& other) const { return !(*this == other); }
-
-        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
-        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
-        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
-
-    private:
-        static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) {
-            if (!rate.isValid()) {
-                // Refresh rate of 0 is a special value which should reset the vote to
-                // its default value.
-                return Seamlessness::Default;
-            } else if (shouldBeSeamless) {
-                return Seamlessness::OnlySeamless;
-            } else {
-                return Seamlessness::SeamedAndSeamless;
-            }
-        }
-    };
+    using FrameRate = scheduler::LayerInfo::FrameRate;
+    using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
 
     struct State {
         Geometry active_legacy;
@@ -262,6 +211,7 @@
         sp<GraphicBuffer> buffer;
         client_cache_t clientCacheId;
         sp<Fence> acquireFence;
+        std::shared_ptr<FenceTime> acquireFenceTime;
         HdrMetadata hdrMetadata;
         Region surfaceDamageRegion;
         int32_t api;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ea92ad8..f4bc2a1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,13 +39,13 @@
 
 namespace {
 
-bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
     // Layers with an explicit vote are always kept active
-    if (layer.getFrameRateForLayerTree().rate.isValid()) {
+    if (info.getSetFrameRateVote().rate.isValid()) {
         return true;
     }
 
-    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+    return info.isVisible() && info.getLastUpdatedTime() >= threshold;
 }
 
 bool traceEnabled() {
@@ -58,11 +58,7 @@
     return atoi(value);
 }
 
-void trace(const wp<Layer>& weak, const LayerInfo& info, LayerHistory::LayerVoteType type,
-           int fps) {
-    const auto layer = weak.promote();
-    if (!layer) return;
-
+void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) {
     const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
         ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
     };
@@ -75,7 +71,7 @@
     traceType(LayerHistory::LayerVoteType::Min, 1);
     traceType(LayerHistory::LayerVoteType::Max, 1);
 
-    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
 }
 } // namespace
 
@@ -88,11 +84,27 @@
 LayerHistory::~LayerHistory() = default;
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
-    auto info = std::make_unique<LayerInfo>(layer->getName(), type);
+    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
+void LayerHistory::deregisterLayer(Layer* layer) {
+    std::lock_guard lock(mLock);
+
+    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+                                 [layer](const auto& pair) { return pair.first == layer; });
+    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+    const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
+    if (i < mActiveLayersEnd) {
+        mActiveLayersEnd--;
+    }
+    const size_t last = mLayerInfos.size() - 1;
+    std::swap(mLayerInfos[i], mLayerInfos[last]);
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
+}
+
 void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
                           LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
@@ -102,7 +114,15 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending);
+    const auto layerProps = LayerInfo::LayerProps{
+            .visible = layer->isVisible(),
+            .bounds = layer->getBounds(),
+            .transform = layer->getTransform(),
+            .setFrameRateVote = layer->getFrameRateForLayerTree(),
+            .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+    };
+
+    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
@@ -119,15 +139,10 @@
     partitionLayers(now);
 
     for (const auto& [layer, info] : activeLayers()) {
-        const auto strong = layer.promote();
-        if (!strong) {
-            continue;
-        }
-
-        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+        const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
         const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
-        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
-              frameRateSelectionPriority, layerFocused ? "" : "not");
+        ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
+              layerFocused ? "" : "not");
 
         const auto vote = info->getRefreshRateVote(now);
         // Skip NoVote layer as those don't have any requirements
@@ -136,18 +151,18 @@
         }
 
         // Compute the layer's position on the screen
-        const Rect bounds = Rect(strong->getBounds());
-        const ui::Transform transform = strong->getTransform();
+        const Rect bounds = Rect(info->getBounds());
+        const ui::Transform transform = info->getTransform();
         constexpr bool roundOutwards = true;
         Rect transformed = transform.transform(bounds, roundOutwards);
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        summary.push_back({strong->getName(), strong->getOwnerUid(), vote.type, vote.fps,
+        summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
                            vote.seamlessness, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(layer, *info, vote.type, vote.fps.getIntValue());
+            trace(*info, vote.type, vote.fps.getIntValue());
         }
     }
 
@@ -160,11 +175,11 @@
     // Collect expired and inactive layers after active layers.
     size_t i = 0;
     while (i < mActiveLayersEnd) {
-        auto& [weak, info] = mLayerInfos[i];
-        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+        auto& [layerUnsafe, info] = mLayerInfos[i];
+        if (isLayerActive(*info, threshold)) {
             i++;
             // Set layer vote if set
-            const auto frameRate = layer->getFrameRateForLayerTree();
+            const auto frameRate = info->getSetFrameRateVote();
             const auto voteType = [&]() {
                 switch (frameRate.type) {
                     case Layer::FrameRateCompatibility::Default:
@@ -179,7 +194,7 @@
             }();
 
             if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
-                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
                 info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
             } else {
                 info->resetLayerVote();
@@ -188,24 +203,12 @@
         }
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
+            trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
         }
 
         info->onLayerInactive(now);
         std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
     }
-
-    // Collect expired layers after inactive layers.
-    size_t end = mLayerInfos.size();
-    while (i < end) {
-        if (mLayerInfos[i].first.promote()) {
-            i++;
-        } else {
-            std::swap(mLayerInfos[i], mLayerInfos[--end]);
-        }
-    }
-
-    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
 }
 
 void LayerHistory::clear() {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 05ecc70..82f6c39 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -70,13 +70,15 @@
     Summary summarize(nsecs_t now);
 
     void clear();
+
+    void deregisterLayer(Layer*);
     std::string dump() const;
 
 private:
     friend LayerHistoryTest;
     friend TestableScheduler;
 
-    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+    using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
     using LayerInfos = std::vector<LayerPair>;
 
     struct ActiveLayers {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 4b4cdae..3630ad8 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -37,17 +37,20 @@
 const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
 bool LayerInfo::sTraceEnabled = false;
 
-LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
+                     LayerHistory::LayerVoteType defaultVote)
       : mName(name),
+        mOwnerUid(ownerUid),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps(0.0f)}),
         mRefreshRateHistory(name) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                                   bool pendingModeChange) {
+                                   bool pendingModeChange, LayerProps props) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
+    mLayerProps = props;
     switch (updateType) {
         case LayerUpdateType::AnimationTX:
             mLastAnimationTime = std::max(lastPresentTime, now);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 40c0214..ba03c89 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <ui/Transform.h>
 #include <utils/Timers.h>
 
 #include <chrono>
@@ -65,22 +66,84 @@
         Seamlessness seamlessness = Seamlessness::Default;
     };
 
+    // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+    // the layer.
+    enum class FrameRateCompatibility {
+        Default, // Layer didn't specify any specific handling strategy
+
+        Exact, // Layer needs the exact frame rate.
+
+        ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+                         // content properly. Any other value will result in a pull down.
+
+        NoVote, // Layer doesn't have any requirements for the refresh rate and
+                // should not be considered when the display refresh rate is determined.
+    };
+
+    // Encapsulates the frame rate and compatibility of the layer. This information will be used
+    // when the display refresh rate is determined.
+    struct FrameRate {
+        using Seamlessness = scheduler::Seamlessness;
+
+        Fps rate;
+        FrameRateCompatibility type;
+        Seamlessness seamlessness;
+
+        FrameRate()
+              : rate(0),
+                type(FrameRateCompatibility::Default),
+                seamlessness(Seamlessness::Default) {}
+        FrameRate(Fps rate, FrameRateCompatibility type,
+                  Seamlessness seamlessness = Seamlessness::OnlySeamless)
+              : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+
+        bool operator==(const FrameRate& other) const {
+            return rate.equalsWithMargin(other.rate) && type == other.type &&
+                    seamlessness == other.seamlessness;
+        }
+
+        bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+        static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+
+    private:
+        static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
+            if (!rate.isValid()) {
+                // Refresh rate of 0 is a special value which should reset the vote to
+                // its default value.
+                return Seamlessness::Default;
+            }
+            return seamlessness;
+        }
+    };
+
     static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
 
     static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
         sRefreshRateConfigs = &refreshRateConfigs;
     }
 
-    LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
+    LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
 
+    struct LayerProps {
+        bool visible = false;
+        FloatRect bounds;
+        ui::Transform transform;
+        FrameRate setFrameRateVote;
+        int32_t frameRateSelectionPriority = -1;
+    };
+
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                            bool pendingModeChange);
+                            bool pendingModeChange, LayerProps props);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -94,12 +157,24 @@
     // Resets the layer vote to its default.
     void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
 
+    std::string getName() const { return mName; }
+
+    uid_t getOwnerUid() const { return mOwnerUid; }
+
     LayerVote getRefreshRateVote(nsecs_t now);
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
+    FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
+    bool isVisible() const { return mLayerProps.visible; }
+    int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
+
+    FloatRect getBounds() const { return mLayerProps.bounds; }
+
+    ui::Transform getTransform() const { return mLayerProps.transform; }
+
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
 
@@ -193,6 +268,7 @@
     bool isFrameTimeValid(const FrameTimeData&) const;
 
     const std::string mName;
+    const uid_t mOwnerUid;
 
     // Used for sanitizing the heuristic data. If two frames are less than
     // this period apart from each other they'll be considered as duplicates.
@@ -217,6 +293,8 @@
     static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
     static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
 
+    LayerProps mLayerProps;
+
     RefreshRateHistory mRefreshRateHistory;
 
     mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index a5cf797..b062acd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -215,7 +215,7 @@
     int explicitExactOrMultipleVoteLayers = 0;
     int explicitExact = 0;
     float maxExplicitWeight = 0;
-    int seamedLayers = 0;
+    int seamedFocusedLayers = 0;
     for (const auto& layer : layers) {
         switch (layer.vote) {
             case LayerVoteType::NoVote:
@@ -243,8 +243,8 @@
                 break;
         }
 
-        if (layer.seamlessness == Seamlessness::SeamedAndSeamless) {
-            seamedLayers++;
+        if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+            seamedFocusedLayers++;
         }
     }
 
@@ -329,12 +329,11 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = seamedLayers > 0
+            const bool isInPolicyForDefault = seamedFocusedLayers > 0
                     ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
                     : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
 
-            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
-                !layer.focused) {
+            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4edbdd2..8426737 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -580,6 +580,12 @@
     mLayerHistory->registerLayer(layer, voteType);
 }
 
+void Scheduler::deregisterLayer(Layer* layer) {
+    if (mLayerHistory) {
+        mLayerHistory->deregisterLayer(layer);
+    }
+}
+
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
     if (mLayerHistory) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0e9eba7..d4932e7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -129,6 +129,7 @@
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
     void setModeChangePending(bool pending);
+    void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b1d63f0..8d592a3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -112,6 +112,7 @@
 #include "FpsReporter.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
+#include "HdrLayerInfoReporter.h"
 #include "Layer.h"
 #include "LayerRenderArea.h"
 #include "LayerVector.h"
@@ -285,6 +286,7 @@
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
+const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
 const String16 sDump("android.permission.DUMP");
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
@@ -753,6 +755,8 @@
         getRenderEngine().primeCache();
     }
 
+    getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+
     // Inform native graphics APIs whether the present timestamp is supported:
 
     const bool presentFenceReliable =
@@ -1492,6 +1496,47 @@
             .get();
 }
 
+status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                                 const sp<gui::IHdrLayerInfoListener>& listener) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+    const auto displayId = display->getId();
+    sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+    if (!hdrInfoReporter) {
+        hdrInfoReporter = sp<HdrLayerInfoReporter>::make();
+    }
+    hdrInfoReporter->addListener(listener);
+    return OK;
+}
+
+status_t SurfaceFlinger::removeHdrLayerInfoListener(
+        const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+    const auto displayId = display->getId();
+    sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+    if (hdrInfoReporter) {
+        hdrInfoReporter->removeListener(listener);
+    }
+    return OK;
+}
+
 status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
     Boost powerBoost = static_cast<Boost>(boostId);
 
@@ -1661,7 +1706,7 @@
     }));
 }
 
-sp<Fence> SurfaceFlinger::previousFrameFence() {
+SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
@@ -1671,9 +1716,9 @@
 
 bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
     ATRACE_CALL();
-    const sp<Fence>& fence = previousFrameFence();
+    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
 
-    if (fence == Fence::NO_FENCE) {
+    if (fence == FenceTime::NO_FENCE) {
         return false;
     }
 
@@ -1684,9 +1729,9 @@
 }
 
 nsecs_t SurfaceFlinger::previousFramePresentTime() {
-    const sp<Fence>& fence = previousFrameFence();
+    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
 
-    if (fence == Fence::NO_FENCE) {
+    if (fence == FenceTime::NO_FENCE) {
         return Fence::SIGNAL_TIME_INVALID;
     }
 
@@ -2117,16 +2162,17 @@
 
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
-    mPreviousPresentFences[0] =
+    mPreviousPresentFences[0].fence =
             display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
-    auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
-    getBE().mDisplayTimeline.push(presentFenceTime);
+    mPreviousPresentFences[0].fenceTime =
+            std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
+
+    getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
 
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
-    mFrameTimeline->setSfPresent(systemTime(),
-                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]),
+    mFrameTimeline->setSfPresent(systemTime(), mPreviousPresentFences[0].fenceTime,
                                  glCompositionDoneFenceTime != FenceTime::NO_FENCE);
 
     nsecs_t dequeueReadyTime = systemTime();
@@ -2140,7 +2186,7 @@
     // be sampled a little later than when we started doing work for this frame,
     // but that should be okay since updateCompositorTiming has snapping logic.
     updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
-                           presentFenceTime);
+                           mPreviousPresentFences[0].fenceTime);
     CompositorTiming compositorTiming;
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2148,26 +2194,74 @@
     }
 
     mDrawingState.traverse([&](Layer* layer) {
-        const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
-                                                           presentFenceTime, compositorTiming);
+        const bool frameLatched =
+                layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                         mPreviousPresentFences[0].fenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
     });
 
+    std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+            hdrInfoListeners;
     {
         Mutex::Autolock lock(mStateLock);
         if (mFpsReporter) {
             mFpsReporter->dispatchLayerFps();
         }
+        hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+        for (auto& [key, value] : mHdrLayerInfoListeners) {
+            if (value && value->hasListeners()) {
+                auto listenersDisplay = getDisplayById(key);
+                if (listenersDisplay) {
+                    hdrInfoListeners.emplace_back(listenersDisplay->getCompositionDisplay(), value);
+                }
+            }
+        }
     }
 
-    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
+    for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
+        HdrLayerInfoReporter::HdrLayerInfo info;
+        int32_t maxArea = 0;
+        mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+            if (layer->isVisible() &&
+                compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
+                bool isHdr = false;
+                switch (layer->getDataSpace()) {
+                    case ui::Dataspace::BT2020:
+                    case ui::Dataspace::BT2020_HLG:
+                    case ui::Dataspace::BT2020_PQ:
+                    case ui::Dataspace::BT2020_ITU:
+                    case ui::Dataspace::BT2020_ITU_HLG:
+                    case ui::Dataspace::BT2020_ITU_PQ:
+                        isHdr = true;
+                        break;
+                    default:
+                        isHdr = false;
+                        break;
+                }
+
+                if (isHdr) {
+                    info.numberOfHdrLayers++;
+                    auto bufferRect = layer->getCompositionState()->geomBufferSize;
+                    int32_t area = bufferRect.width() * bufferRect.height();
+                    if (area > maxArea) {
+                        maxArea = area;
+                        info.maxW = bufferRect.width();
+                        info.maxH = bufferRect.height();
+                    }
+                }
+            }
+        });
+        listener->dispatchHdrLayerInfo(info);
+    }
+
+    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
     mTransactionCallbackInvoker.sendCallbacks();
 
     if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
-        presentFenceTime->isValid()) {
-        mScheduler->addPresentFence(presentFenceTime);
+        mPreviousPresentFences[0].fenceTime->isValid()) {
+        mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
     }
 
     const bool isDisplayConnected =
@@ -2182,9 +2276,8 @@
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        if (presentFenceTime->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(
-                    std::move(presentFenceTime));
+        if (mPreviousPresentFences[0].fenceTime->isValid()) {
+            mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime);
         } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
@@ -2203,7 +2296,7 @@
         mTimeStats->incrementClientCompositionReusedFrames();
     }
 
-    mTimeStats->setPresentFenceGlobal(presentFenceTime);
+    mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime);
 
     const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
     const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
@@ -2634,6 +2727,7 @@
 
     if (display->isPrimary()) {
         mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+        getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
     }
 }
 
@@ -3319,7 +3413,7 @@
     // states) around outside the scope of the lock
     std::vector<const TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> pendingBuffers;
+    std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
     {
         Mutex::Autolock _l(mStateLock);
         {
@@ -3335,10 +3429,13 @@
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
                                                        transaction.originUid, transaction.states,
-                                                       pendingBuffers)) {
+                                                       bufferLayersReadyToPresent)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
+                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                        bufferLayersReadyToPresent.insert(state.surface);
+                    });
                     transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
                 }
@@ -3359,14 +3456,17 @@
                 auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                if (pendingTransactions ||
+                    !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
                                                    transaction.originUid, transaction.states,
-                                                   pendingBuffers) ||
-                    pendingTransactions) {
+                                                   bufferLayersReadyToPresent)) {
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
+                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                        bufferLayersReadyToPresent.insert(state.surface);
+                    });
                     transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop();
@@ -3421,28 +3521,28 @@
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
-        std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+        const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+                bufferLayersReadyToPresent) const {
     ATRACE_CALL();
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-    bool ready = true;
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
     if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
         ATRACE_NAME("not current");
-        ready = false;
+        return false;
     }
 
     if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
         ATRACE_NAME("!isVsyncValid");
-        ready = false;
+        return false;
     }
 
     // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
     // present time of this transaction.
     if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
         ATRACE_NAME("frameIsEarly");
-        ready = false;
+        return false;
     }
 
     for (const ComposerState& state : states) {
@@ -3451,13 +3551,13 @@
         if (acquireFenceChanged && s.acquireFence &&
             s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ATRACE_NAME("fence unsignaled");
-            ready = false;
+            return false;
         }
 
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandleLocked(s.surface).promote();
-        } else if (acquireFenceChanged) {
+        } else if (s.hasBufferChanges()) {
             ALOGW("Transaction with buffer, but no Layer?");
             continue;
         }
@@ -3467,18 +3567,18 @@
 
         ATRACE_NAME(layer->getName().c_str());
 
-        if (acquireFenceChanged) {
+        if (s.hasBufferChanges()) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
             // transaction in the queue.
-            const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
+            const bool hasPendingBuffer =
+                    bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                 ATRACE_NAME("hasPendingBuffer");
-                ready = false;
+                return false;
             }
-            pendingBuffers.insert(s.surface);
         }
     }
-    return ready;
+    return true;
 }
 
 void SurfaceFlinger::queueTransaction(TransactionState& state) {
@@ -3548,13 +3648,6 @@
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
-    // Check for incoming buffer updates and increment the pending buffer count.
-    for (const auto& state : states) {
-        if ((state.state.what & layer_state_t::eAcquireFenceChanged) && (state.state.surface)) {
-            mBufferCountTracker.increment(state.state.surface->localBinder());
-        }
-    }
-
     uint32_t permissions =
             callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
     // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
@@ -3583,6 +3676,11 @@
                            permissions,        hasListenerCallbacks,
                            listenerCallbacks,  originPid,
                            originUid,          transactionId};
+
+    // Check for incoming buffer updates and increment the pending buffer count.
+    state.traverseStatesWithBuffers([&](const layer_state_t& state) {
+        mBufferCountTracker.increment(state.surface->localBinder());
+    });
     queueTransaction(state);
 
     // Check the pending state to make sure the transaction is synchronous.
@@ -3981,13 +4079,16 @@
         }
     }
     if (what & layer_state_t::eFrameRateChanged) {
-        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
-                              "SurfaceFlinger::setClientStateLocked", privileged) &&
-            layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate),
-                                                 Layer::FrameRate::convertCompatibility(
-                                                         s.frameRateCompatibility),
-                                                 s.shouldBeSeamless))) {
-            flags |= eTraversalNeeded;
+        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
+                              "SurfaceFlinger::setClientStateLocked", privileged)) {
+            const auto compatibility =
+                    Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+            const auto strategy =
+                    Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+
+            if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+                flags |= eTraversalNeeded;
+            }
         }
     }
     if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -5107,6 +5208,20 @@
             // This is not sensitive information, so should not require permission control.
             return OK;
         }
+        case ADD_HDR_LAYER_INFO_LISTENER:
+        case REMOVE_HDR_LAYER_INFO_LISTENER: {
+            // TODO (b/183985553): Should getting & setting brightness be part of this...?
+            // codes that require permission check
+            IPCThreadState* ipc = IPCThreadState::self();
+            const int pid = ipc->getCallingPid();
+            const int uid = ipc->getCallingUid();
+            if ((uid != AID_GRAPHICS) &&
+                !PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) {
+                ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid);
+                return PERMISSION_DENIED;
+            }
+            return OK;
+        }
         case ADD_FPS_LISTENER:
         case REMOVE_FPS_LISTENER:
         case ADD_REGION_SAMPLING_LISTENER:
@@ -5668,6 +5783,15 @@
     return getDisplayByLayerStack(displayOrLayerStack);
 }
 
+sp<DisplayDevice> SurfaceFlinger::getDisplayById(DisplayId displayId) const {
+    for (const auto& [token, display] : mDisplays) {
+        if (display->getId() == displayId) {
+            return display;
+        }
+    }
+    return nullptr;
+}
+
 sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
     for (const auto& [token, display] : mDisplays) {
         if (display->getLayerStack() == layerStack) {
@@ -6317,7 +6441,7 @@
     return fromHandleLocked(handle);
 }
 
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) {
+wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
     BBinder* b = nullptr;
     if (handle) {
         b = handle->localBinder();
@@ -6338,6 +6462,7 @@
 }
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+    mScheduler->deregisterLayer(layer);
     mNumLayers--;
     removeFromOffscreenLayers(layer);
 }
@@ -6389,8 +6514,9 @@
 }
 
 status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                                      int8_t compatibility, bool shouldBeSeamless) {
-    if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+                                      int8_t compatibility, int8_t changeFrameRateStrategy) {
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "SurfaceFlinger::setFrameRate")) {
         return BAD_VALUE;
     }
 
@@ -6402,10 +6528,12 @@
                 ALOGE("Attempt to set frame rate on a layer that no longer exists");
                 return BAD_VALUE;
             }
+            const auto strategy =
+                    Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
             if (layer->setFrameRate(
                         Layer::FrameRate(Fps{frameRate},
                                          Layer::FrameRate::convertCompatibility(compatibility),
-                                         shouldBeSeamless))) {
+                                         strategy))) {
                 setTransactionFlags(eTraversalNeeded);
             }
         } else {
@@ -6559,6 +6687,15 @@
     return NO_ERROR;
 }
 
+void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+        std::function<void(const layer_state_t&)> visitor) {
+    for (const auto& state : states) {
+        if (state.state.hasBufferChanges() && (state.state.surface)) {
+            visitor(state.state);
+        }
+    }
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e4ff6c9..c7601fa 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -90,6 +90,7 @@
 class Client;
 class EventThread;
 class FpsReporter;
+class HdrLayerInfoReporter;
 class HWComposer;
 struct SetInputWindowsListener;
 class IGraphicBufferProducer;
@@ -329,7 +330,7 @@
     // Otherwise, returns a weak reference so that callers off the main-thread
     // won't accidentally hold onto the last strong reference.
     wp<Layer> fromHandle(const sp<IBinder>& handle);
-    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
+    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
 
     // Inherit from ClientCache::ErasedRecipient
     void bufferErased(const client_cache_t& clientCacheId) override;
@@ -536,6 +537,8 @@
                 originUid(originUid),
                 id(transactionId) {}
 
+        void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
         FrameTimelineInfo frameTimelineInfo;
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
@@ -684,11 +687,15 @@
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken,
                                   const gui::DisplayBrightness& brightness) override;
+    status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                     const sp<gui::IHdrLayerInfoListener>& listener) override;
+    status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+                                        const sp<gui::IHdrLayerInfoListener>& listener) override;
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                          int8_t compatibility, bool shouldBeSeamless) override;
+                          int8_t compatibility, int8_t changeFrameRateStrategy) override;
     status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
 
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
@@ -841,8 +848,8 @@
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
-            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
-            REQUIRES(mStateLock);
+            const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+                    bufferLayersReadyToPresent) const REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -908,6 +915,7 @@
                                     bool grayscale, ScreenCaptureResults&);
 
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+    sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
 
     // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
@@ -1013,9 +1021,14 @@
 
     bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
 
+    struct FenceWithFenceTime {
+        sp<Fence> fence = Fence::NO_FENCE;
+        std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
+    };
+
     // Gets the fence for the previous frame.
     // Must be called on the main thread.
-    sp<Fence> previousFrameFence();
+    FenceWithFenceTime previousFrameFence();
 
     // Whether the previous frame has not yet been presented to the display.
     // If graceTimeMs is positive, this method waits for at most the provided
@@ -1193,7 +1206,7 @@
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> mLayersWithQueuedFrames;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
-    std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
+    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
     // True if in the previous frame at least one layer was composed via the GPU.
     bool mHadClientComposition = false;
     // True if in the previous frame at least one layer was composed via HW Composer.
@@ -1386,6 +1399,9 @@
     sp<IBinder> mDebugFrameRateFlexibilityToken;
 
     BufferCountTracker mBufferCountTracker;
+
+    std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
+            GUARDED_BY(mStateLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 974ae84..2094972 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -89,12 +89,15 @@
     return byteString;
 }
 
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+        float refreshRate,
+        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+        TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
     util::ProtoOutputStream proto;
     proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+                static_cast<int>(frameRateCompatibility));
+    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
 
     std::string byteString;
     proto.serializeToString(&byteString);
@@ -229,7 +232,10 @@
         mStatsDelegate->statsEventWriteInt32(
                 event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
         mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
-        std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
+        std::string frameRateVoteBytes =
+                frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate,
+                                               layer->setFrameRateVote.frameRateCompatibility,
+                                               layer->setFrameRateVote.seamlessness);
         mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
                                                  frameRateVoteBytes.size()); // set_frame_rate_vote
         std::string appDeadlineMissedBytes =
@@ -468,8 +474,10 @@
 }
 
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                                   std::optional<Fps> renderRate) {
+                                                   std::optional<Fps> renderRate,
+                                                   SetFrameRateVote frameRateVote) {
     ATRACE_CALL();
+    ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
 
     LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
@@ -501,6 +509,9 @@
                 displayStats.stats[layerKey].uid = uid;
                 displayStats.stats[layerKey].layerName = layerName;
             }
+            if (frameRateVote.frameRate > 0.0f) {
+                displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+            }
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
@@ -724,7 +735,8 @@
 }
 
 void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                               Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                               Fps displayRefreshRate, std::optional<Fps> renderRate,
+                               SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -743,12 +755,13 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -768,7 +781,7 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index fd112b9..a87b7cb 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -50,6 +50,8 @@
 
 class TimeStats {
 public:
+    using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
     virtual ~TimeStats() = default;
 
     // Called once boot has been finished to perform additional capabilities,
@@ -110,10 +112,12 @@
     // SetPresent{Time, Fence} are not expected to be called in the critical
     // rendering path, as they flush prior fences if those fences have fired.
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence,
-                                 Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                 SetFrameRateVote frameRateVote) = 0;
 
     // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
     // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -307,10 +311,11 @@
     void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
     void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                        Fps displayRefreshRate, std::optional<Fps> renderRate) override;
+                        Fps displayRefreshRate, std::optional<Fps> renderRate,
+                        SetFrameRateVote frameRateVote) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
-                         std::optional<Fps> renderRate) override;
+                         std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
 
     void incrementJankyFrames(const JankyFramesInfo& info) override;
     // Clean up the layer record
@@ -334,7 +339,8 @@
     AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                            std::optional<Fps> renderRate);
+                                            std::optional<Fps> renderRate,
+                                            SetFrameRateVote frameRateVote);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
     bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d116b02..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -91,6 +91,37 @@
     return result;
 }
 
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+    switch (compatibility) {
+        case FrameRateCompatibility::Undefined:
+            return "Undefined";
+        case FrameRateCompatibility::Default:
+            return "Default";
+        case FrameRateCompatibility::ExactOrMultiple:
+            return "ExactOrMultiple";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+    switch (seamlessness) {
+        case Seamlessness::Undefined:
+            return "Undefined";
+        case Seamlessness::ShouldBeSeamless:
+            return "ShouldBeSeamless";
+        case Seamlessness::NotRequired:
+            return "NotRequired";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+    std::string result;
+    StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+    StringAppendF(&result, "frameRateCompatibility = %s\n",
+                  toString(frameRateCompatibility).c_str());
+    StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+    return result;
+}
+
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
     StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -104,6 +135,8 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     result.append("Jank payload for this layer:\n");
     result.append(jankPayload.toString());
+    result.append("SetFrateRate vote for this layer:\n");
+    result.append(setFrameRateVote.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         const float averageTime = iter->second.averageTime();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5ee28ce..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -55,6 +55,28 @@
         std::string toString() const;
     };
 
+    struct SetFrameRateVote {
+        float frameRate = 0;
+
+        // Needs to be in sync with atoms.proto
+        enum class FrameRateCompatibility {
+            Undefined = 0,
+            Default = 1,
+            ExactOrMultiple = 2,
+        } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+        // Needs to be in sync with atoms.proto
+        enum class Seamlessness {
+            Undefined = 0,
+            ShouldBeSeamless = 1,
+            NotRequired = 2,
+        } seamlessness = Seamlessness::Undefined;
+
+        static std::string toString(FrameRateCompatibility);
+        static std::string toString(Seamlessness);
+        std::string toString() const;
+    };
+
     class TimeStatsLayer {
     public:
         uid_t uid;
@@ -67,6 +89,7 @@
         int32_t lateAcquireFrames = 0;
         int32_t badDesiredPresentFrames = 0;
         JankPayload jankPayload;
+        SetFrameRateVote setFrameRateVote;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e4a1f66..e5c2ec8 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -449,16 +449,16 @@
 
     {
         mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 127, 127, 0);
+        // Child and BG blended. See b/175352694 for tolerance.
+        mCapture->expectColor(Rect(0, 0, 1, 1), Color{127, 127, 0, 255}, 1);
     }
 
     asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
 
     {
         mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 95, 64, 95);
+        // Child and BG blended. See b/175352694 for tolerance.
+        mCapture->expectColor(Rect(0, 0, 1, 1), Color{95, 64, 95, 255}, 1);
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3c1b9d8..9f94c73 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -55,6 +55,7 @@
         "EventThreadTest.cpp",
         "FpsReporterTest.cpp",
         "FpsTest.cpp",
+        "FramebufferSurfaceTest.cpp",
         "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d1385c0..982252f 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -668,9 +668,10 @@
     Fps renderRate = Fps::fromPeriodNsecs(30);
 
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
-                                                                sLayerNameOne, JankType::Unknown,
-                                                                -1, -1, 25}));
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+                                                   JankType::Unknown | JankType::AppDeadlineMissed,
+                                                   -1, -1, 25}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
@@ -697,7 +698,7 @@
     EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
 
     EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
-    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
 }
 
 /*
@@ -1699,9 +1700,9 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
-    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as only
     // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
-    // jank to the SurfaceFrame.
+    // jank to the SurfaceFrame along with AppDeadlineMissed.
 
     EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -1771,7 +1772,8 @@
     EXPECT_EQ(actuals2.presentTime, 60);
     EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
     EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+              JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
 }
 
 TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
diff --git a/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
new file mode 100644
index 0000000..b8df640
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 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 "DisplayHardware/FramebufferSurface.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class FramebufferSurfaceTest : public testing::Test {
+public:
+    ui::Size limitSize(const ui::Size& size, const ui::Size maxSize) {
+        return FramebufferSurface::limitSizeInternal(size, maxSize);
+    }
+};
+
+TEST_F(FramebufferSurfaceTest, limitSize) {
+    const ui::Size kMaxSize(1920, 1080);
+    EXPECT_EQ(ui::Size(1920, 1080), limitSize({3840, 2160}, kMaxSize));
+    EXPECT_EQ(ui::Size(1920, 1080), limitSize({1920, 1080}, kMaxSize));
+    EXPECT_EQ(ui::Size(1920, 1012), limitSize({4096, 2160}, kMaxSize));
+    EXPECT_EQ(ui::Size(1080, 1080), limitSize({3840, 3840}, kMaxSize));
+    EXPECT_EQ(ui::Size(1280, 720), limitSize({1280, 720}, kMaxSize));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index fec590e..b67ebca 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -81,8 +81,8 @@
 
     void setDefaultLayerVote(Layer* layer,
                              LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
-        for (auto& [weak, info] : history().mLayerInfos) {
-            if (auto strong = weak.promote(); strong && strong.get() == layer) {
+        for (auto& [layerUnsafe, info] : history().mLayerInfos) {
+            if (layerUnsafe == layer) {
                 info->setDefaultLayerVote(vote);
                 return;
             }
@@ -180,6 +180,7 @@
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
 
     summary = history().summarize(time);
     EXPECT_TRUE(history().summarize(time).empty());
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index be76e8f..325fb8f 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -40,7 +40,7 @@
 
     auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
 
-    LayerInfo layerInfo{"TestLayerInfo", LayerHistory::LayerVoteType::Heuristic};
+    LayerInfo layerInfo{"TestLayerInfo", 0, LayerHistory::LayerVoteType::Heuristic};
 };
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 15bd0e1..0b70f27 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1182,11 +1182,13 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitching) {
+TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
+    // The default policy doesn't allow group switching. Verify that no
+    // group switches are performed.
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
@@ -1198,64 +1200,203 @@
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
 
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
     RefreshRateConfigs::Policy policy;
     policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = Fps(90.0f);
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
     // Verify that we won't change the group if seamless switch is required.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = Fps(90.0f);
     layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
     layer.desiredRefreshRate = Fps(60.0f);
-    layer.name = "60Hz ExplicitDefault";
     layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // Verify that if the current config is in another group and there are no layers with
     // seamlessness=SeamedAndSeamless we'll go back to the default group.
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
     layer.desiredRefreshRate = Fps(60.0f);
-    layer.name = "60Hz ExplicitDefault";
     layer.seamlessness = Seamlessness::Default;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
+
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=OnlySeamless can't change the mode group.
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    layer.seamlessness = Seamlessness::OnlySeamless;
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].seamlessness = Seamlessness::OnlySeamless;
+    layers[0].name = "60Hz ExplicitDefault";
+    layers[0].focused = true;
 
     layers.push_back(LayerRequirement{.weight = 0.5f});
-    auto& layer2 = layers[layers.size() - 1];
-    layer2.vote = LayerVoteType::ExplicitDefault;
-    layer2.desiredRefreshRate = Fps(90.0f);
-    layer2.name = "90Hz ExplicitDefault";
-    layer2.seamlessness = Seamlessness::SeamedAndSeamless;
-    layer2.focused = false;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].name = "90Hz ExplicitDefault";
+    layers[1].focused = false;
 
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
 
-    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=Default can't change the mode group.
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+    // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
+    // seamlessness=Default can't change the mode group back to the group of the default
+    // mode.
+    // For example, this may happen when a video playback requests and gets a seamed switch,
+    // but another layer (with default seamlessness) starts animating. The animating layer
+    // should not cause a seamed switch.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.1f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].focused = true;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
 }
 
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+    // Layer with seamlessness=Default can change the mode group if there's a not
+    // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+    // when in split screen mode the user switches between the two visible applications.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.7f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].focused = false;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
+    ASSERT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getModeId());
+}
+
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 5c8c2d8..7e602a2 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -475,18 +475,36 @@
                          PrintToStringParamName);
 
 TEST_F(SetFrameRateTest, ValidateFrameRate) {
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, "", /*privileged=*/true));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "",
+                                  /*privileged=*/true));
 
-    EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_FALSE(
-            ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_FALSE(
-            ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
+    EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
 
-    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+    // Invalid compatibility
+    EXPECT_FALSE(
+            ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+    // Invalid change frame rate strategy
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
 }
 
 TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
@@ -505,6 +523,13 @@
     parent->setFrameRate(FRAME_RATE_VOTE1);
     commitTransaction();
 
+    mFlinger.mutableScheduler()
+            .mutableLayerHistory()
+            ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    mFlinger.mutableScheduler()
+            .mutableLayerHistory()
+            ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+
     const auto layerHistorySummary =
             mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0);
     ASSERT_EQ(2u, layerHistorySummary.size());
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a72ab42..c1f0c4e 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -142,15 +142,16 @@
 
     std::string inputCommand(InputCommand cmd, bool useProto);
 
-    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                      TimeStats::SetFrameRateVote frameRateVote);
 
     int32_t genRandomInt32(int32_t begin, int32_t end);
 
     template <size_t N>
     void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
-                          nsecs_t ts) {
+                          nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
         for (size_t i = 0; i < N; i++, ts += 1000000) {
-            setTimeStamp(sequence[i], id, frameNumber, ts);
+            setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
         }
     }
 
@@ -234,7 +235,8 @@
     return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
 }
 
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                                 TimeStats::SetFrameRateVote frameRateVote) {
     switch (type) {
         case TimeStamp::POST:
             ASSERT_NO_FATAL_FAILURE(
@@ -254,13 +256,13 @@
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
             break;
         case TimeStamp::PRESENT:
-            ASSERT_NO_FATAL_FAILURE(
-                    mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+                                                               kRenderRate0, frameRateVote));
             break;
         case TimeStamp::PRESENT_FENCE:
-            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
-                                                                std::make_shared<FenceTime>(ts),
-                                                                kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+                                                kRefreshRate0, kRenderRate0, frameRateVote));
             break;
         default:
             ALOGD("Invalid timestamp type");
@@ -411,6 +413,96 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    const auto frameRateDefault = TimeStats::SetFrameRateVote{
+            .frameRate = 0.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
 TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
     // this stat is not in the proto so verify by checking the string dump
     constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -936,12 +1028,15 @@
     return byteString;
 }
 
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+        float refreshRate,
+        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+        TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
     util::ProtoOutputStream proto;
     proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+                static_cast<int>(frameRateCompatibility));
+    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
 
     std::string byteString;
     proto.serializeToString(&byteString);
@@ -1149,7 +1244,13 @@
     for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
         mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
     }
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
                                       JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
@@ -1181,7 +1282,10 @@
     std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
     std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
     std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
-    std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
+    std::string expectedFrameRateOverride =
+            frameRateVoteToProtoByteString(frameRate60.frameRate,
+                                           frameRate60.frameRateCompatibility,
+                                           frameRate60.seamlessness);
     std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
     {
         InSequence seq;
@@ -1455,7 +1559,7 @@
         TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
         const int32_t ts = genRandomInt32(1, 1000000000);
         ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
-        setTimeStamp(type, layerId, frameNumber, ts);
+        setTimeStamp(type, layerId, frameNumber, ts, {});
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 3e4a0b8..37b74ed 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -48,10 +48,11 @@
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>));
-    MOCK_METHOD5(setPresentFence,
-                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
-                      std::optional<Fps>));
+    MOCK_METHOD6(setPresentTime,
+                 void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+    MOCK_METHOD6(setPresentFence,
+                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+                      SetFrameRateVote));
     MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 866007e..eb24a22 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -108,6 +108,7 @@
 
 cc_binary {
     name: "vr_hwc",
+    enabled: false,
     system_ext_specific: true,
     vintf_fragments: ["manifest_vr_hwc.xml"],
     srcs: [
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index eb0fcc3..5872495 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -363,6 +363,10 @@
   VkJsonDeviceGroup device_group;
   std::vector<VkPhysicalDeviceGroupProperties> group_properties;
   group_properties.resize(count);
+  for (auto& properties : group_properties) {
+    properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
+    properties.pNext = nullptr;
+  }
   result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
                                            group_properties.data());
   if (result != VK_SUCCESS) {