Merge "Initialize uninitialized member in BatteryService" into udc-dev
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index d73a30b..fb69513 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -93,9 +93,22 @@
         libfuzzer_options: [
             "max_len=50000",
         ],
-        cc: [
-            "smoreland@google.com",
-            "waghpawan@google.com",
+    },
+}
+
+// Adding this new fuzzer to test the corpus generated by record_binder
+cc_fuzz {
+    name: "servicemanager_test_fuzzer",
+    defaults: [
+        "servicemanager_defaults",
+        "service_fuzzer_defaults",
+    ],
+    host_supported: true,
+    srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"],
+    fuzz_config: {
+        libfuzzer_options: [
+            "max_len=50000",
         ],
     },
+    corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"],
 }
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index cc8ac0a..63f3821 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -700,6 +700,11 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
     }
 
+    // make sure all callbacks have been told about a consistent state - b/278038751
+    if (serviceIt->second.hasClients) {
+        cb->onClients(service, true);
+    }
+
     mNameToClientCallback[name].push_back(cb);
 
     return Status::ok();
diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
new file mode 100644
index 0000000..e19b6eb
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+#include <utils/StrongPointer.h>
+
+#include "Access.h"
+#include "ServiceManager.h"
+
+using ::android::Access;
+using ::android::Parcel;
+using ::android::ServiceManager;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider(data, size);
+    auto accessPtr = std::make_unique<Access>();
+    auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
+
+    // Reserved bytes
+    provider.ConsumeBytes<uint8_t>(8);
+    uint32_t code = provider.ConsumeIntegral<uint32_t>();
+    uint32_t flag = provider.ConsumeIntegral<uint32_t>();
+    std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>();
+
+    Parcel inputParcel;
+    inputParcel.setData(parcelData.data(), parcelData.size());
+
+    Parcel reply;
+    serviceManager->transact(code, inputParcel, &reply, flag);
+
+    serviceManager->clear();
+
+    return 0;
+}
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
new file mode 100644
index 0000000..e69ab49
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
new file mode 100644
index 0000000..05b27bf
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
new file mode 100644
index 0000000..ff0941b
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
new file mode 100644
index 0000000..7e5f948
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
Binary files differ
diff --git a/include/android/input.h b/include/android/input.h
index d6f9d63..a45f065 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -256,13 +256,13 @@
     AKEY_EVENT_FLAG_LONG_PRESS = 0x80,
 
     /**
-     * Set when a key event has AKEY_EVENT_FLAG_CANCELED set because a long
+     * Set when a key event has #AKEY_EVENT_FLAG_CANCELED set because a long
      * press action was executed while it was down.
      */
     AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100,
 
     /**
-     * Set for AKEY_EVENT_ACTION_UP when this event's key code is still being
+     * Set for #AKEY_EVENT_ACTION_UP when this event's key code is still being
      * tracked from its initial down.  That is, somebody requested that tracking
      * started on the key down and a long press has not caused
      * the tracking to be canceled.
@@ -282,7 +282,7 @@
 
 /**
  * Bit shift for the action bits holding the pointer index as
- * defined by AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
+ * defined by #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
  */
 #define AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8
 
@@ -293,8 +293,8 @@
 
     /**
      * Bits in the action code that represent a pointer index, used with
-     * AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP.  Shifting
-     * down by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
+     * #AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP.  Shifting
+     * down by #AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
      * index where the data for the pointer going up or down can be found.
      */
     AMOTION_EVENT_ACTION_POINTER_INDEX_MASK  = 0xff00,
@@ -309,8 +309,8 @@
     AMOTION_EVENT_ACTION_UP = 1,
 
     /**
-     * A change has happened during a press gesture (between AMOTION_EVENT_ACTION_DOWN and
-     * AMOTION_EVENT_ACTION_UP).  The motion contains the most recent point, as well as
+     * A change has happened during a press gesture (between #AMOTION_EVENT_ACTION_DOWN and
+     * #AMOTION_EVENT_ACTION_UP).  The motion contains the most recent point, as well as
      * any intermediate points since the last down or move event.
      */
     AMOTION_EVENT_ACTION_MOVE = 2,
@@ -330,18 +330,18 @@
 
     /**
      * A non-primary pointer has gone down.
-     * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
+     * The bits in #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
      */
     AMOTION_EVENT_ACTION_POINTER_DOWN = 5,
 
     /**
      * A non-primary pointer has gone up.
-     * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
+     * The bits in #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
      */
     AMOTION_EVENT_ACTION_POINTER_UP = 6,
 
     /**
-     * A change happened but the pointer is not down (unlike AMOTION_EVENT_ACTION_MOVE).
+     * A change happened but the pointer is not down (unlike #AMOTION_EVENT_ACTION_MOVE).
      * The motion contains the most recent point, as well as any intermediate points since
      * the last hover move event.
      */
@@ -349,8 +349,8 @@
 
     /**
      * The motion event contains relative vertical and/or horizontal scroll offsets.
-     * Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL
-     * and AMOTION_EVENT_AXIS_HSCROLL.
+     * Use {@link AMotionEvent_getAxisValue} to retrieve the information from
+     * #AMOTION_EVENT_AXIS_VSCROLL and #AMOTION_EVENT_AXIS_HSCROLL.
      * The pointer may or may not be down when this event is dispatched.
      * This action is always delivered to the winder under the pointer, which
      * may not be the window currently touched.
@@ -535,7 +535,7 @@
      * is pointing in relation to the vertical axis of the current orientation of the screen.
      * The range is from -PI radians to PI radians, where 0 is pointing up,
      * -PI/2 radians is pointing left, -PI or PI radians is pointing down, and PI/2 radians
-     * is pointing right.  See also {@link AMOTION_EVENT_AXIS_TILT}.
+     * is pointing right.  See also #AMOTION_EVENT_AXIS_TILT.
      */
     AMOTION_EVENT_AXIS_ORIENTATION = 8,
     /**
@@ -688,7 +688,7 @@
     /**
      * Axis constant: The movement of y position of a motion event.
      *
-     * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position.
+     * Same as #AMOTION_EVENT_AXIS_RELATIVE_X, but for y position.
      */
     AMOTION_EVENT_AXIS_RELATIVE_Y = 28,
     /**
@@ -883,7 +883,8 @@
      * Classification constant: Ambiguous gesture.
      *
      * The user's intent with respect to the current event stream is not yet determined. Events
-     * starting in AMBIGUOUS_GESTURE will eventually resolve into either DEEP_PRESS or NONE.
+     * starting in #AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE will eventually resolve into
+     * either #AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS or #AMOTION_EVENT_CLASSIFICATION_NONE.
      * Gestural actions, such as scrolling, should be inhibited until the classification resolves
      * to another value or the event stream ends.
      */
@@ -1006,7 +1007,8 @@
  * Refer to the documentation on android.view.InputDevice for more details about input sources
  * and their correct interpretation.
  *
- * @deprecated These constants are deprecated. Use {@link AMOTION_EVENT_AXIS AMOTION_EVENT_AXIS_*} constants instead.
+ * @deprecated These constants are deprecated. Use {@link AMOTION_EVENT_AXIS AMOTION_EVENT_AXIS_*}
+ * constants instead.
  */
 enum {
     /** x */
@@ -1060,8 +1062,8 @@
 /**
  * Releases interface objects created by {@link AKeyEvent_fromJava()}
  * and {@link AMotionEvent_fromJava()}.
- * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
- * The underlying Java object remains valid and does not change its state.
+ * After returning, the specified {@link AInputEvent}* object becomes invalid and should no longer
+ * be used. The underlying Java object remains valid and does not change its state.
  *
  * Available since API level 31.
  */
@@ -1114,9 +1116,10 @@
 int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
 
 /**
- * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
- * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
- * returned by this function must be disposed using {@link AInputEvent_release()}.
+ * Creates a native {@link AInputEvent}* object that is a copy of the specified Java
+ * android.view.KeyEvent. The result may be used with generic and KeyEvent-specific AInputEvent_*
+ * functions. The object returned by this function must be disposed using
+ * {@link AInputEvent_release()}.
  *
  * Available since API level 31.
  */
@@ -1304,7 +1307,7 @@
 /**
  * Get the number of historical points in this event.  These are movements that
  * have occurred between this event and the previous event.  This only applies
- * to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
+ * to #AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
  * Historical samples are indexed from oldest to newest.
  */
 size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event);
@@ -1465,7 +1468,7 @@
         __INTRODUCED_IN(__ANDROID_API_T__);
 
 /**
- * Creates a native AInputEvent* object that is a copy of the specified Java
+ * Creates a native {@link AInputEvent}* object that is a copy of the specified Java
  * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
  * AInputEvent_* functions. The object returned by this function must be disposed using
  * {@link AInputEvent_release()}.
@@ -1485,7 +1488,7 @@
 
 /**
  * Add this input queue to a looper for processing.  See
- * ALooper_addFd() for information on the ident, callback, and data params.
+ * {@link ALooper_addFd()} for information on the ident, callback, and data params.
  */
 void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
         int ident, ALooper_callbackFunc callback, void* data);
@@ -1520,12 +1523,12 @@
 
 /**
  * Report that dispatching has finished with the given event.
- * This must be called after receiving an event with AInputQueue_get_event().
+ * This must be called after receiving an event with {@link AInputQueue_getEvent()}.
  */
 void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
 
 /**
- * Returns the AInputQueue* object associated with the supplied Java InputQueue
+ * Returns the {@link AInputQueue}* object associated with the supplied Java InputQueue
  * object. The returned native object holds a weak reference to the Java object,
  * and is only valid as long as the Java object has not yet been disposed. You
  * should ensure that there is a strong reference to the Java object and that it
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index daeebec..e4ba58a 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -548,11 +548,15 @@
  *                     to the max display brightness. The system may not be able to, or may choose
  *                     not to, deliver the requested range.
  *
- *                     If unspecified, the system will attempt to provide the best range it can
- *                     for the given ambient conditions & device state. However, voluntarily
- *                     reducing the requested range can help improve battery life as well as can
- *                     improve quality by ensuring greater bit depth is allocated to the luminance
- *                     range in use.
+ *                     While requesting a large desired ratio will result in the most
+ *                     dynamic range, voluntarily reducing the requested range can help
+ *                     improve battery life as well as can improve quality by ensuring
+ *                     greater bit depth is allocated to the luminance range in use.
+ *
+ *                     Default value is 1.0f and indicates that extended range brightness
+ *                     is not being used, so the resulting SDR or HDR behavior will be
+ *                     determined entirely by the dataspace being used (ie, typically SDR
+ *                     however PQ or HLG transfer functions will still result in HDR)
  *
  *                     Must be finite && >= 1.0f
  *
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
index c5ff03b..c0f6768 100644
--- a/include/ftl/algorithm.h
+++ b/include/ftl/algorithm.h
@@ -68,4 +68,29 @@
   return std::cref(pair.second);
 }
 
+// Combinator for ftl::Optional<T>::or_else when T is std::reference_wrapper<const V>. Given a
+// lambda argument that returns a `constexpr` value, ftl::static_ref<T> binds a reference to a
+// static T initialized to that constant.
+//
+//   const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv);
+//   assert("???"sv ==
+//          map.get(20).or_else(ftl::static_ref<std::string_view>([] { return "???"sv; }))->get());
+//
+//   using Map = decltype(map);
+//
+//   assert("snow cone"sv ==
+//          ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; })
+//              .transform(ftl::to_mapped_ref<Map>)
+//              .or_else(ftl::static_ref<std::string_view>([] { return "snow cone"sv; }))
+//              ->get());
+//
+template <typename T, typename F>
+constexpr auto static_ref(F&& f) {
+  return [f = std::forward<F>(f)] {
+    constexpr auto kInitializer = f();
+    static const T kValue = kInitializer;
+    return Optional(std::cref(kValue));
+  };
+}
+
 }  // namespace android::ftl
diff --git a/include/ftl/flags.h b/include/ftl/flags.h
index cdb4e84..dbe3148 100644
--- a/include/ftl/flags.h
+++ b/include/ftl/flags.h
@@ -120,7 +120,7 @@
     }
 
     /* Tests whether any of the given flags are set */
-    bool any(Flags<F> f) const { return (mFlags & f.mFlags) != 0; }
+    bool any(Flags<F> f = ~Flags<F>()) const { return (mFlags & f.mFlags) != 0; }
 
     /* Tests whether all of the given flags are set */
     bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; }
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 66d3435..1a40fdb 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -212,6 +212,11 @@
     std::string languageTag;
     // The layout type such as QWERTY or AZERTY.
     std::string layoutType;
+
+    inline bool operator==(const KeyboardLayoutInfo& other) const {
+        return languageTag == other.languageTag && layoutType == other.layoutType;
+    }
+    inline bool operator!=(const KeyboardLayoutInfo& other) const { return !(*this == other); }
 };
 
 // The version of the Universal Stylus Initiative (USI) protocol supported by the input device.
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index c67310e..9423041 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -227,7 +227,7 @@
         status_t parseMapKey();
         status_t parseKey();
         status_t parseKeyProperty();
-        status_t finishKey(Key* key);
+        status_t finishKey(Key& key);
         status_t parseModifier(const std::string& token, int32_t* outMetaState);
         status_t parseCharacterLiteral(char16_t* outCharacter);
     };
@@ -243,7 +243,7 @@
 
     KeyCharacterMap(const std::string& filename);
 
-    bool getKey(int32_t keyCode, const Key** outKey) const;
+    const Key* getKey(int32_t keyCode) const;
     const Behavior* getKeyBehavior(int32_t keyCode, int32_t metaState) const;
     static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState);
 
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 13ffb58..21a2877 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -34,10 +34,12 @@
 
 protected:
     const android::base::unique_fd mFd;
-    bool writeInputEvent(uint16_t type, uint16_t code, int32_t value);
+    bool writeInputEvent(uint16_t type, uint16_t code, int32_t value,
+                         std::chrono::nanoseconds eventTime);
     bool writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
                          const std::map<int, int>& evKeyCodeMapping,
-                         const std::map<int, UinputAction>& actionMapping);
+                         const std::map<int, UinputAction>& actionMapping,
+                         std::chrono::nanoseconds eventTime);
 };
 
 class VirtualKeyboard : public VirtualInputDevice {
@@ -47,7 +49,8 @@
     static const std::map<int, UinputAction> KEY_ACTION_MAPPING;
     VirtualKeyboard(android::base::unique_fd fd);
     virtual ~VirtualKeyboard() override;
-    bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+    bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+                       std::chrono::nanoseconds eventTime);
 };
 
 class VirtualDpad : public VirtualInputDevice {
@@ -55,17 +58,20 @@
     static const std::map<int, int> DPAD_KEY_CODE_MAPPING;
     VirtualDpad(android::base::unique_fd fd);
     virtual ~VirtualDpad() override;
-    bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+    bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+                           std::chrono::nanoseconds eventTime);
 };
 
 class VirtualMouse : public VirtualInputDevice {
 public:
     VirtualMouse(android::base::unique_fd fd);
     virtual ~VirtualMouse() override;
-    bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction);
+    bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+                          std::chrono::nanoseconds eventTime);
     // TODO(b/259554911): changing float parameters to int32_t.
-    bool writeRelativeEvent(float relativeX, float relativeY);
-    bool writeScrollEvent(float xAxisMovement, float yAxisMovement);
+    bool writeRelativeEvent(float relativeX, float relativeY, std::chrono::nanoseconds eventTime);
+    bool writeScrollEvent(float xAxisMovement, float yAxisMovement,
+                          std::chrono::nanoseconds eventTime);
 
 private:
     static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING;
@@ -78,7 +84,8 @@
     virtual ~VirtualTouchscreen() override;
     // TODO(b/259554911): changing float parameters to int32_t.
     bool writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action, float locationX,
-                         float locationY, float pressure, float majorAxisSize);
+                         float locationY, float pressure, float majorAxisSize,
+                         std::chrono::nanoseconds eventTime);
 
 private:
     static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING;
@@ -91,7 +98,7 @@
      */
     std::bitset<MAX_POINTERS> mActivePointers{};
     bool isValidPointerId(int32_t pointerId, UinputAction uinputAction);
-    bool handleTouchDown(int32_t pointerId);
-    bool handleTouchUp(int32_t pointerId);
+    bool handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime);
+    bool handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime);
 };
 } // namespace android
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index ef58ed3..1c76135 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -131,12 +131,12 @@
         return std::nullopt;
     }
 
-    if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) {
+    if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
         LOG(ERROR) << "Failed to set sent parcel data.";
         return std::nullopt;
     }
 
-    if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) {
+    if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
         LOG(ERROR) << "Failed to set reply parcel data.";
         return std::nullopt;
     }
@@ -349,11 +349,11 @@
         return UNKNOWN_ERROR;
     }
 
-    if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) {
+    if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
         LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
-    if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) {
+    if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
         LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index ed3ce24..03fa699 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -928,7 +928,7 @@
                                           transactionData.size() -
                                                   offsetof(RpcWireTransaction, data)};
         Span<const uint32_t> objectTableSpan;
-        if (session->getProtocolVersion().value() >
+        if (session->getProtocolVersion().value() >=
             RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
             std::optional<Span<const uint8_t>> objectTableBytes =
                     parcelSpan.splitOff(transaction->parcelDataSize);
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a323feb..cb64603 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,9 +37,9 @@
 class RpcTransport;
 class FdTrigger;
 
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 2;
 constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 1;
 
 // Starting with this version:
 //
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 3dab2c7..642cea4 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -237,14 +237,25 @@
     checkRepr(kCurrentRepr, 0);
 }
 
+TEST(RpcWire, V1) {
+    checkRepr(kCurrentRepr, 1);
+}
+
 TEST(RpcWire, CurrentVersion) {
     checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
 }
 
-static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL,
+static_assert(RPC_WIRE_PROTOCOL_VERSION == 1,
               "If the binder wire protocol is updated, this test should test additional versions. "
               "The binder wire protocol should only be updated on upstream AOSP.");
 
+TEST(RpcWire, NextIsPlusOneReminder) {
+    if (RPC_WIRE_PROTOCOL_VERSION != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        EXPECT_EQ(RPC_WIRE_PROTOCOL_VERSION + 1, RPC_WIRE_PROTOCOL_VERSION_NEXT)
+                << "Make sure to note what the next version should be.";
+    }
+}
+
 TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) {
     if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
         EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL")
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 3a99606..68b0008 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -65,6 +65,10 @@
     mTipcPort.msg_queue_len = 6; // Three each way
     mTipcPort.priv = this;
 
+    // TODO(b/266741352): follow-up to prevent needing this in the future
+    // Trusty needs to be set to the latest stable version that is in prebuilts there.
+    mRpcServer->setProtocolVersion(0);
+
     if (mPortAcl) {
         // Initialize the array of pointers to uuids.
         // The pointers in mUuidPtrs should stay valid across moves of
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 97cb810..5eb3308 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -62,7 +62,10 @@
         "android.hardware.audio@7.0::IDevicesFactory",
         "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
         "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
+        "android.hardware.automotive.can@1.0::ICanBus",
+        "android.hardware.automotive.can@1.0::ICanController",
         "android.hardware.automotive.evs@1.0::IEvsCamera",
+        "android.hardware.automotive.sv@1.0::ISurroundViewService",
         "android.hardware.automotive.vehicle@2.0::IVehicle",
         "android.hardware.biometrics.face@1.0::IBiometricsFace",
         "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
@@ -87,7 +90,12 @@
 /* list of hal interface to dump containing process during native dumps */
 static const std::vector<std::string> aidl_interfaces_to_dump {
         "android.hardware.automotive.audiocontrol.IAudioControl",
+        "android.hardware.automotive.can.ICanController",
         "android.hardware.automotive.evs.IEvsEnumerator",
+        "android.hardware.automotive.ivn.IIvnAndroidDevice",
+        "android.hardware.automotive.occupant_awareness.IOccupantAwareness",
+        "android.hardware.automotive.remoteaccess.IRemoteAccess",
+        "android.hardware.automotive.vehicle.IVehicle",
         "android.hardware.biometrics.face.IBiometricsFace",
         "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
         "android.hardware.camera.provider.ICameraProvider",
diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp
index 8052caf..487b1b8 100644
--- a/libs/ftl/algorithm_test.cpp
+++ b/libs/ftl/algorithm_test.cpp
@@ -47,4 +47,20 @@
   EXPECT_EQ(opt->get(), ftl::StaticVector("tiramisu"sv));
 }
 
+TEST(Algorithm, StaticRef) {
+  using namespace std::string_view_literals;
+
+  const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv);
+  ASSERT_EQ("???"sv,
+            map.get(20).or_else(ftl::static_ref<std::string_view>([] { return "???"sv; }))->get());
+
+  using Map = decltype(map);
+
+  ASSERT_EQ("snow cone"sv,
+            ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; })
+                .transform(ftl::to_mapped_ref<Map>)
+                .or_else(ftl::static_ref<std::string_view>([] { return "snow cone"sv; }))
+                ->get());
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/flags_test.cpp b/libs/ftl/flags_test.cpp
index eea052b..1279d11 100644
--- a/libs/ftl/flags_test.cpp
+++ b/libs/ftl/flags_test.cpp
@@ -35,6 +35,7 @@
 
 TEST(Flags, Any) {
     Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_TRUE(flags.any());
     ASSERT_TRUE(flags.any(TestFlags::ONE));
     ASSERT_TRUE(flags.any(TestFlags::TWO));
     ASSERT_FALSE(flags.any(TestFlags::THREE));
@@ -42,6 +43,9 @@
     ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
     ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
     ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+
+    Flags<TestFlags> emptyFlags;
+    ASSERT_FALSE(emptyFlags.any());
 }
 
 TEST(Flags, All) {
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 46dd62d..c480056 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -689,4 +689,13 @@
     return mAngleNamespace;
 }
 
+void GraphicsEnv::nativeToggleAngleAsSystemDriver(bool enabled) {
+    const sp<IGpuService> gpuService = getGpuService();
+    if (!gpuService) {
+        ALOGE("No GPU service");
+        return;
+    }
+    gpuService->toggleAngleAsSystemDriver(enabled);
+}
+
 } // namespace android
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index ceb52f7..4c070ae 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -78,6 +78,15 @@
                            IBinder::FLAG_ONEWAY);
     }
 
+    void toggleAngleAsSystemDriver(bool enabled) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+        data.writeBool(enabled);
+
+        remote()->transact(BnGpuService::TOGGLE_ANGLE_AS_SYSTEM_DRIVER, data, &reply,
+                           IBinder::FLAG_ONEWAY);
+    }
+
     std::string getUpdatableDriverPath() override {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -189,6 +198,15 @@
 
             return OK;
         }
+        case TOGGLE_ANGLE_AS_SYSTEM_DRIVER: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            bool enableAngleAsSystemDriver;
+            if ((status = data.readBool(&enableAngleAsSystemDriver)) != OK) return status;
+
+            toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
+            return OK;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index b58a6d9..1274c46 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -142,6 +142,8 @@
     const std::string& getDebugLayers();
     // Get the debug layers to load.
     const std::string& getDebugLayersGLES();
+    // Set the persist.graphics.egl system property value.
+    void nativeToggleAngleAsSystemDriver(bool enabled);
 
 private:
     enum UseAngle { UNKNOWN, YES, NO };
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index b708b0f..e3857d2 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -50,6 +50,9 @@
     // setter and getter for updatable driver path.
     virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
     virtual std::string getUpdatableDriverPath() = 0;
+
+    // sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true.
+    virtual void toggleAngleAsSystemDriver(bool enabled) = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
@@ -59,6 +62,7 @@
         SET_TARGET_STATS,
         SET_UPDATABLE_DRIVER_PATH,
         GET_UPDATABLE_DRIVER_PATH,
+        TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 21900a0..0a63c15 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -66,6 +66,18 @@
     ],
 }
 
+filegroup {
+    name: "android_gui_aidl",
+    srcs: [
+        "android/gui/DisplayInfo.aidl",
+        "android/gui/FocusRequest.aidl",
+        "android/gui/InputApplicationInfo.aidl",
+        "android/gui/IWindowInfosListener.aidl",
+        "android/gui/IWindowInfosReportedListener.aidl",
+        "android/gui/WindowInfo.aidl",
+    ],
+}
+
 cc_library_static {
     name: "libgui_window_info_static",
     vendor_available: true,
@@ -118,6 +130,9 @@
     name: "libgui_aidl",
     srcs: ["aidl/**/*.aidl"],
     path: "aidl/",
+    aidl: {
+        deps: [":android_gui_aidl"],
+    },
 }
 
 filegroup {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 7f7a043..5217209 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -33,6 +33,7 @@
 #include <gui/BufferQueueCore.h>
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
+#include <gui/TraceUtils.h>
 
 #include <private/gui/BufferQueueThreadState.h>
 #ifndef __ANDROID_VNDK__
@@ -646,7 +647,7 @@
 
 status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
         int maxAcquiredBuffers) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
 
     if (maxAcquiredBuffers < 1 ||
             maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9eb1a9f..9a2343b 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -35,6 +35,7 @@
 #include <gui/GLConsumer.h>
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
+#include <gui/TraceUtils.h>
 #include <private/gui/BufferQueueThreadState.h>
 
 #include <utils/Log.h>
@@ -125,7 +126,7 @@
 
 status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
                                                         int* maxBufferCount) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s(%d)", __func__, maxDequeuedBuffers);
     BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
             maxDequeuedBuffers);
 
@@ -502,6 +503,20 @@
         if ((buffer == nullptr) ||
                 buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
         {
+            if (CC_UNLIKELY(ATRACE_ENABLED())) {
+                if (buffer == nullptr) {
+                    ATRACE_FORMAT_INSTANT("%s buffer reallocation: null", mConsumerName.string());
+                } else {
+                    ATRACE_FORMAT_INSTANT("%s buffer reallocation actual %dx%d format:%d "
+                                          "layerCount:%d "
+                                          "usage:%d requested: %dx%d format:%d layerCount:%d "
+                                          "usage:%d ",
+                                          mConsumerName.string(), width, height, format,
+                                          BQ_LAYER_COUNT, usage, buffer->getWidth(),
+                                          buffer->getHeight(), buffer->getPixelFormat(),
+                                          buffer->getLayerCount(), buffer->getUsage());
+                }
+            }
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = nullptr;
             mSlots[found].mRequestBufferCalled = false;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7700aa4..eb5cc4f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -898,7 +898,7 @@
 }
 
 void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
-    if (!(state.what & layer_state_t::eBufferChanged)) {
+    if (!(state.what & layer_state_t::eBufferChanged) || !state.bufferData->hasBuffer()) {
         return;
     }
 
@@ -1642,28 +1642,25 @@
 
     releaseBufferIfOverwriting(*s);
 
-    if (buffer == nullptr) {
-        s->what &= ~layer_state_t::eBufferChanged;
-        s->bufferData = nullptr;
-        return *this;
-    }
-
     std::shared_ptr<BufferData> bufferData = std::make_shared<BufferData>();
     bufferData->buffer = buffer;
-    uint64_t frameNumber = sc->resolveFrameNumber(optFrameNumber);
-    bufferData->frameNumber = frameNumber;
-    bufferData->producerId = producerId;
-    bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
-    if (fence) {
-        bufferData->acquireFence = *fence;
-        bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
+    if (buffer) {
+        uint64_t frameNumber = sc->resolveFrameNumber(optFrameNumber);
+        bufferData->frameNumber = frameNumber;
+        bufferData->producerId = producerId;
+        bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
+        if (fence) {
+            bufferData->acquireFence = *fence;
+            bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
+        }
+        bufferData->releaseBufferEndpoint =
+                IInterface::asBinder(TransactionCompletedListener::getIInstance());
+        setReleaseBufferCallback(bufferData.get(), callback);
     }
-    bufferData->releaseBufferEndpoint =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
-    setReleaseBufferCallback(bufferData.get(), callback);
     s->what |= layer_state_t::eBufferChanged;
     s->bufferData = std::move(bufferData);
     registerSurfaceControlForCallback(sc);
@@ -1684,6 +1681,25 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::unsetBuffer(
+        const sp<SurfaceControl>& sc) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    if (!(s->what & layer_state_t::eBufferChanged)) {
+        return *this;
+    }
+
+    releaseBufferIfOverwriting(*s);
+
+    s->what &= ~layer_state_t::eBufferChanged;
+    s->bufferData = nullptr;
+    return *this;
+}
+
 void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
                                                                   ReleaseBufferCallback callback) {
     if (!callback) {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d431b43..945b164 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -541,6 +541,7 @@
                                const std::optional<sp<Fence>>& fence = std::nullopt,
                                const std::optional<uint64_t>& frameNumber = std::nullopt,
                                uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr);
+        Transaction& unsetBuffer(const sp<SurfaceControl>& sc);
         std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
 
         /**
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 08785b4..1c900e9 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -51,6 +51,7 @@
     enum Buffer {
         NOT_ACQUIRED = 0,
         ACQUIRED,
+        ACQUIRED_NULL,
     };
 
     enum PreviousBuffer {
@@ -133,17 +134,28 @@
               : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
 
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
-                                       nsecs_t latchTime) const {
+                                       nsecs_t /* latchTime */) const {
             const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
                          previousReleaseFence, transformHint, frameEvents, ignore] =
-                surfaceControlStats;
+                    surfaceControlStats;
 
-            ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
-            ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
-                      mBufferResult == ExpectedResult::Buffer::ACQUIRED)
-                    << "bad acquire time";
-            ASSERT_LE(std::get<nsecs_t>(acquireTimeOrFence), latchTime)
-                    << "acquire time should be <= latch time";
+            nsecs_t acquireTime = -1;
+            if (std::holds_alternative<nsecs_t>(acquireTimeOrFence)) {
+                acquireTime = std::get<nsecs_t>(acquireTimeOrFence);
+            } else {
+                auto fence = std::get<sp<Fence>>(acquireTimeOrFence);
+                if (fence) {
+                    ASSERT_EQ(fence->wait(3000), NO_ERROR);
+                    acquireTime = fence->getSignalTime();
+                }
+            }
+
+            if (mBufferResult == ExpectedResult::Buffer::ACQUIRED) {
+                ASSERT_GT(acquireTime, 0) << "acquire time should be valid";
+            } else {
+                ASSERT_LE(acquireTime, 0) << "acquire time should not be valid";
+            }
+            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED);
 
             if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
                 ASSERT_NE(previousReleaseFence, nullptr)
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 7067c11..a3ad680 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -179,13 +179,13 @@
     BLASTBufferQueueTest() {
         const ::testing::TestInfo* const testInfo =
                 ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+        ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
     }
 
     ~BLASTBufferQueueTest() {
         const ::testing::TestInfo* const testInfo =
                 ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
+        ALOGD("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
     }
 
     void SetUp() {
@@ -206,7 +206,7 @@
         const ui::Size& resolution = displayState.layerStackSpaceRect;
         mDisplayWidth = resolution.getWidth();
         mDisplayHeight = resolution.getHeight();
-        ALOGV("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
+        ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
               displayState.orientation);
 
         mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index fc6551c..6880678 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -68,7 +68,7 @@
     void HandleBufferFreed() {
         std::lock_guard<std::mutex> lock(mMutex);
         mFreedBufferCount++;
-        ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
+        ALOGD("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
     }
 
     void DequeueBuffer(int* outSlot) {
@@ -80,7 +80,7 @@
                                                 nullptr, nullptr);
         ASSERT_GE(ret, 0);
 
-        ALOGV("dequeueBuffer: slot=%d", slot);
+        ALOGD("dequeueBuffer: slot=%d", slot);
         if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
             ret = mProducer->requestBuffer(slot, &mBuffers[slot]);
             ASSERT_EQ(NO_ERROR, ret);
@@ -89,7 +89,7 @@
     }
 
     void QueueBuffer(int slot) {
-        ALOGV("enqueueBuffer: slot=%d", slot);
+        ALOGD("enqueueBuffer: slot=%d", slot);
         IGraphicBufferProducer::QueueBufferInput bufferInput(
             0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
@@ -104,12 +104,12 @@
         status_t ret = mBIC->acquireBuffer(&buffer, 0, false);
         ASSERT_EQ(NO_ERROR, ret);
 
-        ALOGV("acquireBuffer: slot=%d", buffer.mSlot);
+        ALOGD("acquireBuffer: slot=%d", buffer.mSlot);
         *outSlot = buffer.mSlot;
     }
 
     void ReleaseBuffer(int slot) {
-        ALOGV("releaseBuffer: slot=%d", slot);
+        ALOGD("releaseBuffer: slot=%d", slot);
         BufferItem buffer;
         buffer.mSlot = slot;
         buffer.mGraphicBuffer = mBuffers[slot];
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index d1208ee..2f1fd3e 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -49,14 +49,14 @@
     BufferQueueTest() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+        ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 
     ~BufferQueueTest() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("End test:   %s.%s", testInfo->test_case_name(),
+        ALOGD("End test:   %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 00e32d9..0a14afa 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -62,7 +62,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         CpuConsumerTestParams params = GetParam();
-        ALOGV("** Starting test %s (%d x %d, %d, 0x%x)",
+        ALOGD("** Starting test %s (%d x %d, %d, 0x%x)",
                 test_info->name(),
                 params.width, params.height,
                 params.maxLockedBuffers, params.format);
@@ -582,7 +582,7 @@
     uint32_t stride[numInQueue];
 
     for (int i = 0; i < numInQueue; i++) {
-        ALOGV("Producing frame %d", i);
+        ALOGD("Producing frame %d", i);
         ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time[i],
                         &stride[i]));
     }
@@ -590,7 +590,7 @@
     // Consume
 
     for (int i = 0; i < numInQueue; i++) {
-        ALOGV("Consuming frame %d", i);
+        ALOGD("Consuming frame %d", i);
         CpuConsumer::LockedBuffer b;
         err = mCC->lockNextBuffer(&b);
         ASSERT_NO_ERROR(err, "getNextBuffer error: ");
@@ -624,7 +624,7 @@
     uint32_t stride;
 
     for (int i = 0; i < params.maxLockedBuffers + 1; i++) {
-        ALOGV("Producing frame %d", i);
+        ALOGD("Producing frame %d", i);
         ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time,
                         &stride));
     }
@@ -633,7 +633,7 @@
 
     std::vector<CpuConsumer::LockedBuffer> b(params.maxLockedBuffers);
     for (int i = 0; i < params.maxLockedBuffers; i++) {
-        ALOGV("Locking frame %d", i);
+        ALOGD("Locking frame %d", i);
         err = mCC->lockNextBuffer(&b[i]);
         ASSERT_NO_ERROR(err, "getNextBuffer error: ");
 
@@ -647,16 +647,16 @@
         checkAnyBuffer(b[i], GetParam().format);
     }
 
-    ALOGV("Locking frame %d (too many)", params.maxLockedBuffers);
+    ALOGD("Locking frame %d (too many)", params.maxLockedBuffers);
     CpuConsumer::LockedBuffer bTooMuch;
     err = mCC->lockNextBuffer(&bTooMuch);
     ASSERT_TRUE(err == NOT_ENOUGH_DATA) << "Allowing too many locks";
 
-    ALOGV("Unlocking frame 0");
+    ALOGD("Unlocking frame 0");
     err = mCC->unlockBuffer(b[0]);
     ASSERT_NO_ERROR(err, "Could not unlock buffer 0: ");
 
-    ALOGV("Locking frame %d (should work now)", params.maxLockedBuffers);
+    ALOGD("Locking frame %d (should work now)", params.maxLockedBuffers);
     err = mCC->lockNextBuffer(&bTooMuch);
     ASSERT_NO_ERROR(err, "Did not allow new lock after unlock");
 
@@ -669,11 +669,11 @@
 
     checkAnyBuffer(bTooMuch, GetParam().format);
 
-    ALOGV("Unlocking extra buffer");
+    ALOGD("Unlocking extra buffer");
     err = mCC->unlockBuffer(bTooMuch);
     ASSERT_NO_ERROR(err, "Could not unlock extra buffer: ");
 
-    ALOGV("Locking frame %d (no more available)", params.maxLockedBuffers + 1);
+    ALOGD("Locking frame %d (no more available)", params.maxLockedBuffers + 1);
     err = mCC->lockNextBuffer(&b[0]);
     ASSERT_EQ(BAD_VALUE, err) << "Not out of buffers somehow";
 
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index a1405fc..3ae4b6d 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -31,7 +31,7 @@
 void GLTest::SetUp() {
     const ::testing::TestInfo* const testInfo =
         ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+    ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
 
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
@@ -135,7 +135,7 @@
 
     const ::testing::TestInfo* const testInfo =
         ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGV("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
+    ALOGD("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
 }
 
 EGLint const* GLTest::getConfigAttribs() {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 3427731..e6cb89c 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -84,7 +84,7 @@
     virtual void SetUp() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+        ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
                 testInfo->name());
 
         mMC = new MockConsumer;
@@ -114,7 +114,7 @@
     virtual void TearDown() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("End test:   %s.%s", testInfo->test_case_name(),
+        ALOGD("End test:   %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index b65cdda..2f14924 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -36,14 +36,14 @@
     StreamSplitterTest() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+        ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 
     ~StreamSplitterTest() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("End test:   %s.%s", testInfo->test_case_name(),
+        ALOGD("End test:   %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 };
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index c7458a3..82b6697 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -42,7 +42,7 @@
     virtual void SetUp() {
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+        ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
                 testInfo->name());
 
         sp<IGraphicBufferProducer> producer;
@@ -99,7 +99,7 @@
 
         const ::testing::TestInfo* const testInfo =
             ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGV("End test:   %s.%s", testInfo->test_case_name(),
+        ALOGD("End test:   %s.%s", testInfo->test_case_name(),
                 testInfo->name());
     }
 
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 737bd15..f703901 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -272,8 +272,8 @@
 
 char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
     char16_t result = 0;
-    const Key* key;
-    if (getKey(keyCode, &key)) {
+    const Key* key = getKey(keyCode);
+    if (key != nullptr) {
         result = key->label;
     }
 #if DEBUG_MAPPING
@@ -284,8 +284,8 @@
 
 char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
     char16_t result = 0;
-    const Key* key;
-    if (getKey(keyCode, &key)) {
+    const Key* key = getKey(keyCode);
+    if (key != nullptr) {
         result = key->number;
     }
 #if DEBUG_MAPPING
@@ -332,8 +332,8 @@
 char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
         int32_t metaState) const {
     char16_t result = 0;
-    const Key* key;
-    if (getKey(keyCode, &key)) {
+    const Key* key = getKey(keyCode);
+    if (key != nullptr) {
         // Try to find the most general behavior that maps to this character.
         // For example, the base key behavior will usually be last in the list.
         // However, if we find a perfect meta state match for one behavior then use that one.
@@ -493,19 +493,18 @@
     return std::make_pair(toKeyCode, toMetaState);
 }
 
-bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
+const KeyCharacterMap::Key* KeyCharacterMap::getKey(int32_t keyCode) const {
     ssize_t index = mKeys.indexOfKey(keyCode);
     if (index >= 0) {
-        *outKey = mKeys.valueAt(index);
-        return true;
+        return mKeys.valueAt(index);
     }
-    return false;
+    return nullptr;
 }
 
 const KeyCharacterMap::Behavior* KeyCharacterMap::getKeyBehavior(int32_t keyCode,
                                                                  int32_t metaState) const {
-    const Key* key;
-    if (getKey(keyCode, &key)) {
+    const Key* key = getKey(keyCode);
+    if (key != nullptr) {
         for (const Behavior& behavior : key->behaviors) {
             if (matchesMetaState(metaState, behavior.metaState)) {
                 return &behavior;
@@ -1048,17 +1047,17 @@
     String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
     if (token == "}") {
         mState = STATE_TOP;
-        return finishKey(key);
+        return finishKey(*key);
     }
 
-    Vector<Property> properties;
+    std::vector<Property> properties;
 
     // Parse all comma-delimited property names up to the first colon.
     for (;;) {
         if (token == "label") {
-            properties.add(Property(PROPERTY_LABEL));
+            properties.emplace_back(PROPERTY_LABEL);
         } else if (token == "number") {
-            properties.add(Property(PROPERTY_NUMBER));
+            properties.emplace_back(PROPERTY_NUMBER);
         } else {
             int32_t metaState;
             status_t status = parseModifier(token.string(), &metaState);
@@ -1067,7 +1066,7 @@
                         mTokenizer->getLocation().string(), token.string());
                 return status;
             }
-            properties.add(Property(PROPERTY_META, metaState));
+            properties.emplace_back(PROPERTY_META, metaState);
         }
 
         mTokenizer->skipDelimiters(WHITESPACE);
@@ -1182,8 +1181,7 @@
     } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
 
     // Add the behavior.
-    for (size_t i = 0; i < properties.size(); i++) {
-        const Property& property = properties.itemAt(i);
+    for (const Property& property : properties) {
         switch (property.property) {
         case PROPERTY_LABEL:
             if (key->label) {
@@ -1230,12 +1228,12 @@
     return NO_ERROR;
 }
 
-status_t KeyCharacterMap::Parser::finishKey(Key* key) {
+status_t KeyCharacterMap::Parser::finishKey(Key& key) {
     // Fill in default number property.
-    if (!key->number) {
+    if (!key.number) {
         char16_t digit = 0;
         char16_t symbol = 0;
-        for (const Behavior& b : key->behaviors) {
+        for (const Behavior& b : key.behaviors) {
             char16_t ch = b.character;
             if (ch) {
                 if (ch >= '0' && ch <= '9') {
@@ -1247,7 +1245,7 @@
                 }
             }
         }
-        key->number = digit ? digit : symbol;
+        key.number = digit ? digit : symbol;
     }
     return NO_ERROR;
 }
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 3c1f2b6..af9ce3a 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -44,15 +44,24 @@
     ioctl(mFd, UI_DEV_DESTROY);
 }
 
-bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value) {
-    struct input_event ev = {.type = type, .code = code, .value = value};
+bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value,
+                                         std::chrono::nanoseconds eventTime) {
+    std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(eventTime);
+    std::chrono::microseconds microseconds =
+            std::chrono::duration_cast<std::chrono::microseconds>(eventTime - seconds);
+    struct input_event ev = {.type = type,
+                             .code = code,
+                             .value = value,
+                             .input_event_sec = static_cast<time_t>(seconds.count()),
+                             .input_event_usec = static_cast<suseconds_t>(microseconds.count())};
     return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
 }
 
 /** Utility method to write keyboard key events or mouse button events. */
 bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
                                          const std::map<int, int>& evKeyCodeMapping,
-                                         const std::map<int, UinputAction>& actionMapping) {
+                                         const std::map<int, UinputAction>& actionMapping,
+                                         std::chrono::nanoseconds eventTime) {
     auto evKeyCodeIterator = evKeyCodeMapping.find(androidCode);
     if (evKeyCodeIterator == evKeyCodeMapping.end()) {
         ALOGE("Unsupported native EV keycode for android code %d", androidCode);
@@ -63,10 +72,10 @@
         return false;
     }
     if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second),
-                         static_cast<int32_t>(actionIterator->second))) {
+                         static_cast<int32_t>(actionIterator->second), eventTime)) {
         return false;
     }
-    if (!writeInputEvent(EV_SYN, SYN_REPORT, 0)) {
+    if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
         return false;
     }
     return true;
@@ -189,8 +198,10 @@
 VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
 VirtualKeyboard::~VirtualKeyboard() {}
 
-bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
-    return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING);
+bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+                                    std::chrono::nanoseconds eventTime) {
+    return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING,
+                           eventTime);
 }
 
 // --- VirtualDpad ---
@@ -210,9 +221,10 @@
 
 VirtualDpad::~VirtualDpad() {}
 
-bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
+bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+                                    std::chrono::nanoseconds eventTime) {
     return writeEvKeyEvent(androidKeyCode, androidAction, DPAD_KEY_CODE_MAPPING,
-                           VirtualKeyboard::KEY_ACTION_MAPPING);
+                           VirtualKeyboard::KEY_ACTION_MAPPING, eventTime);
 }
 
 // --- VirtualMouse ---
@@ -236,20 +248,24 @@
 
 VirtualMouse::~VirtualMouse() {}
 
-bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction) {
+bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+                                    std::chrono::nanoseconds eventTime) {
     return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
-                           BUTTON_ACTION_MAPPING);
+                           BUTTON_ACTION_MAPPING, eventTime);
 }
 
-bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY) {
-    return writeInputEvent(EV_REL, REL_X, relativeX) && writeInputEvent(EV_REL, REL_Y, relativeY) &&
-            writeInputEvent(EV_SYN, SYN_REPORT, 0);
+bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY,
+                                      std::chrono::nanoseconds eventTime) {
+    return writeInputEvent(EV_REL, REL_X, relativeX, eventTime) &&
+            writeInputEvent(EV_REL, REL_Y, relativeY, eventTime) &&
+            writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
 }
 
-bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement) {
-    return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement) &&
-            writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement) &&
-            writeInputEvent(EV_SYN, SYN_REPORT, 0);
+bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement,
+                                    std::chrono::nanoseconds eventTime) {
+    return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement, eventTime) &&
+            writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement, eventTime) &&
+            writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
 }
 
 // --- VirtualTouchscreen ---
@@ -291,7 +307,7 @@
 
 bool VirtualTouchscreen::writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action,
                                          float locationX, float locationY, float pressure,
-                                         float majorAxisSize) {
+                                         float majorAxisSize, std::chrono::nanoseconds eventTime) {
     auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
     if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
         return false;
@@ -300,44 +316,44 @@
     if (!isValidPointerId(pointerId, uinputAction)) {
         return false;
     }
-    if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId)) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId, eventTime)) {
         return false;
     }
     auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
     if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
         return false;
     }
-    if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE,
-                         static_cast<int32_t>(toolTypeIterator->second))) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE, static_cast<int32_t>(toolTypeIterator->second),
+                         eventTime)) {
         return false;
     }
-    if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId)) {
+    if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId, eventTime)) {
         return false;
     }
-    if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId)) {
+    if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId, eventTime)) {
         return false;
     }
-    if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX)) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX, eventTime)) {
         return false;
     }
-    if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY)) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY, eventTime)) {
         return false;
     }
     if (!isnan(pressure)) {
-        if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure)) {
+        if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure, eventTime)) {
             return false;
         }
     }
     if (!isnan(majorAxisSize)) {
-        if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
+        if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize, eventTime)) {
             return false;
         }
     }
-    return writeInputEvent(EV_SYN, SYN_REPORT, 0);
+    return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
 }
 
-bool VirtualTouchscreen::handleTouchUp(int32_t pointerId) {
-    if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) {
+bool VirtualTouchscreen::handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1), eventTime)) {
         return false;
     }
     // When a pointer is no longer in touch, remove the pointer id from the corresponding
@@ -347,7 +363,8 @@
 
     // Only sends the BTN UP event when there's no pointers on the touchscreen.
     if (mActivePointers.none()) {
-        if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) {
+        if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE),
+                             eventTime)) {
             return false;
         }
         ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", mFd.get());
@@ -355,12 +372,13 @@
     return true;
 }
 
-bool VirtualTouchscreen::handleTouchDown(int32_t pointerId) {
+bool VirtualTouchscreen::handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime) {
     // When a new pointer is down on the touchscreen, add the pointer id in the corresponding
     // entry in the unreleased touches map.
     if (mActivePointers.none()) {
         // Only sends the BTN Down event when the first pointer on the touchscreen is down.
-        if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) {
+        if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS),
+                             eventTime)) {
             return false;
         }
         ALOGD_IF(isDebug(), "First pointer %d down under touchscreen %d, BTN DOWN event sent",
@@ -369,7 +387,7 @@
 
     mActivePointers.set(pointerId);
     ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, mFd.get());
-    if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) {
+    if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId), eventTime)) {
         return false;
     }
     return true;
diff --git a/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp b/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp
deleted file mode 100644
index 095ac2f..0000000
--- a/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2022 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 <jpegrecoverymap/jpegencoderhelper.h>
-#include <gtest/gtest.h>
-#include <utils/Log.h>
-
-#include <fcntl.h>
-
-namespace android::jpegrecoverymap {
-
-#define VALID_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
-#define VALID_IMAGE_WIDTH 320
-#define VALID_IMAGE_HEIGHT 240
-#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y"
-#define SINGLE_CHANNEL_IMAGE_WIDTH VALID_IMAGE_WIDTH
-#define SINGLE_CHANNEL_IMAGE_HEIGHT VALID_IMAGE_HEIGHT
-#define INVALID_SIZE_IMAGE "/sdcard/Documents/minnie-318x240.yu12"
-#define INVALID_SIZE_IMAGE_WIDTH 318
-#define INVALID_SIZE_IMAGE_HEIGHT 240
-#define JPEG_QUALITY 90
-
-class JpegEncoderHelperTest : public testing::Test {
-public:
-    struct Image {
-        std::unique_ptr<uint8_t[]> buffer;
-        size_t width;
-        size_t height;
-    };
-    JpegEncoderHelperTest();
-    ~JpegEncoderHelperTest();
-protected:
-    virtual void SetUp();
-    virtual void TearDown();
-
-    Image mValidImage, mInvalidSizeImage, mSingleChannelImage;
-};
-
-JpegEncoderHelperTest::JpegEncoderHelperTest() {}
-
-JpegEncoderHelperTest::~JpegEncoderHelperTest() {}
-
-static size_t getFileSize(int fd) {
-    struct stat st;
-    if (fstat(fd, &st) < 0) {
-        ALOGW("%s : fstat failed", __func__);
-        return 0;
-    }
-    return st.st_size; // bytes
-}
-
-static bool loadFile(const char filename[], JpegEncoderHelperTest::Image* result) {
-    int fd = open(filename, O_CLOEXEC);
-    if (fd < 0) {
-        return false;
-    }
-    int length = getFileSize(fd);
-    if (length == 0) {
-        close(fd);
-        return false;
-    }
-    result->buffer.reset(new uint8_t[length]);
-    if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
-        close(fd);
-        return false;
-    }
-    close(fd);
-    return true;
-}
-
-void JpegEncoderHelperTest::SetUp() {
-    if (!loadFile(VALID_IMAGE, &mValidImage)) {
-        FAIL() << "Load file " << VALID_IMAGE << " failed";
-    }
-    mValidImage.width = VALID_IMAGE_WIDTH;
-    mValidImage.height = VALID_IMAGE_HEIGHT;
-    if (!loadFile(INVALID_SIZE_IMAGE, &mInvalidSizeImage)) {
-        FAIL() << "Load file " << INVALID_SIZE_IMAGE << " failed";
-    }
-    mInvalidSizeImage.width = INVALID_SIZE_IMAGE_WIDTH;
-    mInvalidSizeImage.height = INVALID_SIZE_IMAGE_HEIGHT;
-    if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
-        FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
-    }
-    mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
-    mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
-}
-
-void JpegEncoderHelperTest::TearDown() {}
-
-TEST_F(JpegEncoderHelperTest, validImage) {
-    JpegEncoderHelper encoder;
-    EXPECT_TRUE(encoder.compressImage(mValidImage.buffer.get(), mValidImage.width,
-                                         mValidImage.height, JPEG_QUALITY, NULL, 0));
-    ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-TEST_F(JpegEncoderHelperTest, invalidSizeImage) {
-    JpegEncoderHelper encoder;
-    EXPECT_FALSE(encoder.compressImage(mInvalidSizeImage.buffer.get(), mInvalidSizeImage.width,
-                                          mInvalidSizeImage.height, JPEG_QUALITY, NULL, 0));
-}
-
-TEST_F(JpegEncoderHelperTest, singleChannelImage) {
-    JpegEncoderHelper encoder;
-    EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width,
-                                         mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true));
-    ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-}  // namespace android::jpegrecoverymap
-
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 5c122d4..932be56 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -82,6 +82,18 @@
     textureRelease->unref(false);
 }
 
+void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace,
+                     SkColorType colorType) {
+    GrGLTextureInfo textureInfo;
+    bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+    LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+                     "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i texType: %i"
+                     "\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u colorType %i",
+                     msg, tex.isValid(), dataspace, tex.width(), tex.height(), tex.hasMipmaps(),
+                     tex.isProtected(), static_cast<int>(tex.textureType()), retrievedTextureInfo,
+                     textureInfo.fTarget, textureInfo.fFormat, colorType);
+}
+
 sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
                                              GrDirectContext* context) {
     ATRACE_CALL();
@@ -107,9 +119,9 @@
 
     mImage = image;
     mDataspace = dataspace;
-    LOG_ALWAYS_FATAL_IF(mImage == nullptr,
-                        "Unable to generate SkImage. isTextureValid:%d dataspace:%d",
-                        mBackendTexture.isValid(), dataspace);
+    if (!mImage) {
+        logFatalTexture("Unable to generate SkImage.", mBackendTexture, dataspace, colorType);
+    }
     return mImage;
 }
 
@@ -131,9 +143,9 @@
     }
 
     mDataspace = dataspace;
-    LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
-                        "Unable to generate SkSurface. isTextureValid:%d dataspace:%d",
-                        mBackendTexture.isValid(), dataspace);
+    if (!mSurface) {
+        logFatalTexture("Unable to generate SkSurface.", mBackendTexture, dataspace, mColorType);
+    }
     return mSurface;
 }
 
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 936e316..b99e385 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -52,6 +52,20 @@
     PFN_vkDestroyInstance vkDestroyInstance = nullptr;
 };
 
+// Ref-Count a semaphore
+struct DestroySemaphoreInfo {
+    VkSemaphore mSemaphore;
+    // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia
+    // (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two refs, one
+    // owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented each time
+    // delete_semaphore* is called with this object. Skia will call destroy_semaphore* once it is
+    // done with the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine
+    // calls delete_semaphore* after sending the semaphore to Skia and exporting it if need be.
+    int mRefs = 2;
+
+    DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {}
+};
+
 struct VulkanInterface {
     bool initialized = false;
     VkInstance instance;
@@ -588,14 +602,22 @@
     return true;
 }
 
-static void delete_semaphore(void* _semaphore) {
-    VkSemaphore semaphore = (VkSemaphore)_semaphore;
-    sVulkanInterface.destroySemaphore(semaphore);
+static void delete_semaphore(void* semaphore) {
+    DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore);
+    --info->mRefs;
+    if (!info->mRefs) {
+        sVulkanInterface.destroySemaphore(info->mSemaphore);
+        delete info;
+    }
 }
 
-static void delete_semaphore_protected(void* _semaphore) {
-    VkSemaphore semaphore = (VkSemaphore)_semaphore;
-    sProtectedContentVulkanInterface.destroySemaphore(semaphore);
+static void delete_semaphore_protected(void* semaphore) {
+    DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore);
+    --info->mRefs;
+    if (!info->mRefs) {
+        sProtectedContentVulkanInterface.destroySemaphore(info->mSemaphore);
+        delete info;
+    }
 }
 
 static VulkanInterface& getVulkanInterface(bool protectedContext) {
@@ -624,19 +646,30 @@
 }
 
 base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
-    VkSemaphore signalSemaphore = getVulkanInterface(isProtected()).createExportableSemaphore();
-    GrBackendSemaphore beSignalSemaphore;
-    beSignalSemaphore.initVulkan(signalSemaphore);
+    VulkanInterface& vi = getVulkanInterface(isProtected());
+    VkSemaphore semaphore = vi.createExportableSemaphore();
+
+    GrBackendSemaphore backendSemaphore;
+    backendSemaphore.initVulkan(semaphore);
+
     GrFlushInfo flushInfo;
-    flushInfo.fNumSemaphores = 1;
-    flushInfo.fSignalSemaphores = &beSignalSemaphore;
-    flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore;
-    flushInfo.fFinishedContext = (void*)signalSemaphore;
+    DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
+    if (semaphore != VK_NULL_HANDLE) {
+        destroySemaphoreInfo = new DestroySemaphoreInfo(semaphore);
+        flushInfo.fNumSemaphores = 1;
+        flushInfo.fSignalSemaphores = &backendSemaphore;
+        flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore;
+        flushInfo.fFinishedContext = destroySemaphoreInfo;
+    }
     GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
     grContext->submit(false /* no cpu sync */);
     int drawFenceFd = -1;
-    if (GrSemaphoresSubmitted::kYes == submitted) {
-        drawFenceFd = getVulkanInterface(isProtected()).exportSemaphoreSyncFd(signalSemaphore);
+    if (semaphore != VK_NULL_HANDLE) {
+        if (GrSemaphoresSubmitted::kYes == submitted) {
+            drawFenceFd = vi.exportSemaphoreSyncFd(semaphore);
+        }
+        // Now that drawFenceFd has been created, we can delete our reference to this semaphore
+        flushInfo.fFinishedProc(destroySemaphoreInfo);
     }
     base::unique_fd res(drawFenceFd);
     return res;
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/ultrahdr/Android.bp
similarity index 97%
rename from libs/jpegrecoverymap/Android.bp
rename to libs/ultrahdr/Android.bp
index a376ced..e3f709b 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/ultrahdr/Android.bp
@@ -22,7 +22,7 @@
 }
 
 cc_library {
-    name: "libjpegrecoverymap",
+    name: "libultrahdr",
     host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/ultrahdr/OWNERS
similarity index 100%
rename from libs/jpegrecoverymap/OWNERS
rename to libs/ultrahdr/OWNERS
diff --git a/libs/jpegrecoverymap/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp
similarity index 94%
rename from libs/jpegrecoverymap/gainmapmath.cpp
rename to libs/ultrahdr/gainmapmath.cpp
index f15a078..37c3cf3 100644
--- a/libs/jpegrecoverymap/gainmapmath.cpp
+++ b/libs/ultrahdr/gainmapmath.cpp
@@ -16,9 +16,9 @@
 
 #include <cmath>
 #include <vector>
-#include <jpegrecoverymap/gainmapmath.h>
+#include <ultrahdr/gainmapmath.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 static const std::vector<float> kPqOETF = [] {
     std::vector<float> result;
@@ -396,45 +396,46 @@
 
 // TODO: confirm we always want to convert like this before calculating
 // luminance.
-ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
+ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut,
+                                    ultrahdr_color_gamut hdr_gamut) {
   switch (sdr_gamut) {
-    case JPEGR_COLORGAMUT_BT709:
+    case ULTRAHDR_COLORGAMUT_BT709:
       switch (hdr_gamut) {
-        case JPEGR_COLORGAMUT_BT709:
+        case ULTRAHDR_COLORGAMUT_BT709:
           return identityConversion;
-        case JPEGR_COLORGAMUT_P3:
+        case ULTRAHDR_COLORGAMUT_P3:
           return p3ToBt709;
-        case JPEGR_COLORGAMUT_BT2100:
+        case ULTRAHDR_COLORGAMUT_BT2100:
           return bt2100ToBt709;
-        case JPEGR_COLORGAMUT_UNSPECIFIED:
+        case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
           return nullptr;
       }
       break;
-    case JPEGR_COLORGAMUT_P3:
+    case ULTRAHDR_COLORGAMUT_P3:
       switch (hdr_gamut) {
-        case JPEGR_COLORGAMUT_BT709:
+        case ULTRAHDR_COLORGAMUT_BT709:
           return bt709ToP3;
-        case JPEGR_COLORGAMUT_P3:
+        case ULTRAHDR_COLORGAMUT_P3:
           return identityConversion;
-        case JPEGR_COLORGAMUT_BT2100:
+        case ULTRAHDR_COLORGAMUT_BT2100:
           return bt2100ToP3;
-        case JPEGR_COLORGAMUT_UNSPECIFIED:
+        case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
           return nullptr;
       }
       break;
-    case JPEGR_COLORGAMUT_BT2100:
+    case ULTRAHDR_COLORGAMUT_BT2100:
       switch (hdr_gamut) {
-        case JPEGR_COLORGAMUT_BT709:
+        case ULTRAHDR_COLORGAMUT_BT709:
           return bt709ToBt2100;
-        case JPEGR_COLORGAMUT_P3:
+        case ULTRAHDR_COLORGAMUT_P3:
           return p3ToBt2100;
-        case JPEGR_COLORGAMUT_BT2100:
+        case ULTRAHDR_COLORGAMUT_BT2100:
           return identityConversion;
-        case JPEGR_COLORGAMUT_UNSPECIFIED:
+        case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
           return nullptr;
       }
       break;
-    case JPEGR_COLORGAMUT_UNSPECIFIED:
+    case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
       return nullptr;
   }
 }
@@ -442,12 +443,12 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // Gain map calculations
-uint8_t encodeGain(float y_sdr, float y_hdr, jr_metadata_ptr metadata) {
+uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata) {
   return encodeGain(y_sdr, y_hdr, metadata,
                     log2(metadata->minContentBoost), log2(metadata->maxContentBoost));
 }
 
-uint8_t encodeGain(float y_sdr, float y_hdr, jr_metadata_ptr metadata,
+uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
                    float log2MinContentBoost, float log2MaxContentBoost) {
   float gain = 1.0f;
   if (y_sdr > 0.0f) {
@@ -462,14 +463,14 @@
                             * 255.0f);
 }
 
-Color applyGain(Color e, float gain, jr_metadata_ptr metadata) {
+Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata) {
   float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
                  + log2(metadata->maxContentBoost) * gain;
   float gainFactor = exp2(logBoost);
   return e * gainFactor;
 }
 
-Color applyGain(Color e, float gain, jr_metadata_ptr metadata, float displayBoost) {
+Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost) {
   float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
                  + log2(metadata->maxContentBoost) * gain;
   float gainFactor = exp2(logBoost * displayBoost / metadata->maxContentBoost);
@@ -511,7 +512,7 @@
     chroma_stride = luma_stride;
   }
   if (chroma_data == nullptr) {
-    chroma_data = &reinterpret_cast<uint16_t*>(image->data)[image->luma_stride * image->height];
+    chroma_data = &reinterpret_cast<uint16_t*>(image->data)[luma_stride * image->height];
   }
 
   size_t pixel_y_idx = y * luma_stride + x;
@@ -662,4 +663,4 @@
        | (((uint64_t) floatToHalf(1.0f)) << 48);
 }
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
diff --git a/libs/jpegrecoverymap/icc.cpp b/libs/ultrahdr/icc.cpp
similarity index 92%
rename from libs/jpegrecoverymap/icc.cpp
rename to libs/ultrahdr/icc.cpp
index 6e78f67..c807705 100644
--- a/libs/jpegrecoverymap/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/icc.h>
-#include <jpegrecoverymap/gainmapmath.h>
+#include <ultrahdr/icc.h>
+#include <ultrahdr/gainmapmath.h>
 #include <vector>
 #include <utils/Log.h>
 
@@ -23,7 +23,7 @@
 #define FLT_MAX 0x1.fffffep127f
 #endif
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
     float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
     float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
@@ -127,17 +127,17 @@
     }
 }
 
-std::string IccHelper::get_desc_string(const jpegr_transfer_function tf,
-                                       const jpegr_color_gamut gamut) {
+std::string IccHelper::get_desc_string(const ultrahdr_transfer_function tf,
+                                       const ultrahdr_color_gamut gamut) {
     std::string result;
     switch (gamut) {
-        case JPEGR_COLORGAMUT_BT709:
+        case ULTRAHDR_COLORGAMUT_BT709:
             result += "sRGB";
             break;
-        case JPEGR_COLORGAMUT_P3:
+        case ULTRAHDR_COLORGAMUT_P3:
             result += "Display P3";
             break;
-        case JPEGR_COLORGAMUT_BT2100:
+        case ULTRAHDR_COLORGAMUT_BT2100:
             result += "Rec2020";
             break;
         default:
@@ -146,16 +146,16 @@
     }
     result += " Gamut with ";
     switch (tf) {
-        case JPEGR_TF_SRGB:
+        case ULTRAHDR_TF_SRGB:
             result += "sRGB";
             break;
-        case JPEGR_TF_LINEAR:
+        case ULTRAHDR_TF_LINEAR:
             result += "Linear";
             break;
-        case JPEGR_TF_PQ:
+        case ULTRAHDR_TF_PQ:
             result += "PQ";
             break;
-        case JPEGR_TF_HLG:
+        case ULTRAHDR_TF_HLG:
             result += "HLG";
             break;
         default:
@@ -234,11 +234,11 @@
     return dataStruct;
 }
 
-float IccHelper::compute_tone_map_gain(const jpegr_transfer_function tf, float L) {
+float IccHelper::compute_tone_map_gain(const ultrahdr_transfer_function tf, float L) {
     if (L <= 0.f) {
         return 1.f;
     }
-    if (tf == JPEGR_TF_PQ) {
+    if (tf == ULTRAHDR_TF_PQ) {
         // The PQ transfer function will map to the range [0, 1]. Linearly scale
         // it up to the range [0, 10,000/203]. We will then tone map that back
         // down to [0, 1].
@@ -251,7 +251,7 @@
         constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
         return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
     }
-    if (tf == JPEGR_TF_HLG) {
+    if (tf == ULTRAHDR_TF_HLG) {
         // Let Lw be the brightness of the display in nits.
         constexpr float Lw = 203.f;
         const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
@@ -295,7 +295,7 @@
     float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
 
     // Compute the tone map gain based on the luminance.
-    float tone_map_gain = compute_tone_map_gain(JPEGR_TF_PQ, L);
+    float tone_map_gain = compute_tone_map_gain(ULTRAHDR_TF_PQ, L);
 
     // Apply the tone map gain.
     for (size_t i = 0; i < kNumChannels; ++i) {
@@ -397,7 +397,8 @@
     return dataStruct;
 }
 
-sp<DataStruct> IccHelper::writeIccProfile(jpegr_transfer_function tf, jpegr_color_gamut gamut) {
+sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf,
+                                          ultrahdr_color_gamut gamut) {
     ICCHeader header;
 
     std::vector<std::pair<uint32_t, sp<DataStruct>>> tags;
@@ -409,13 +410,13 @@
 
     Matrix3x3 toXYZD50;
     switch (gamut) {
-        case JPEGR_COLORGAMUT_BT709:
+        case ULTRAHDR_COLORGAMUT_BT709:
             toXYZD50 = kSRGB;
             break;
-        case JPEGR_COLORGAMUT_P3:
+        case ULTRAHDR_COLORGAMUT_P3:
             toXYZD50 = kDisplayP3;
             break;
-        case JPEGR_COLORGAMUT_BT2100:
+        case ULTRAHDR_COLORGAMUT_BT2100:
             toXYZD50 = kRec2020;
             break;
         default:
@@ -437,8 +438,8 @@
     tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
 
     // Compute transfer curves.
-    if (tf != JPEGR_TF_PQ) {
-        if (tf == JPEGR_TF_HLG) {
+    if (tf != ULTRAHDR_TF_PQ) {
+        if (tf == ULTRAHDR_TF_HLG) {
             std::vector<uint8_t> trc_table;
             trc_table.resize(kTrcTableSize * 2);
             for (uint32_t i = 0; i < kTrcTableSize; ++i) {
@@ -462,32 +463,32 @@
     }
 
     // Compute CICP.
-    if (tf == JPEGR_TF_HLG || tf == JPEGR_TF_PQ) {
+    if (tf == ULTRAHDR_TF_HLG || tf == ULTRAHDR_TF_PQ) {
         // The CICP tag is present in ICC 4.4, so update the header's version.
         header.version = Endian_SwapBE32(0x04400000);
 
         uint32_t color_primaries = 0;
-        if (gamut == JPEGR_COLORGAMUT_BT709) {
+        if (gamut == ULTRAHDR_COLORGAMUT_BT709) {
             color_primaries = kCICPPrimariesSRGB;
-        } else if (gamut == JPEGR_COLORGAMUT_P3) {
+        } else if (gamut == ULTRAHDR_COLORGAMUT_P3) {
             color_primaries = kCICPPrimariesP3;
         }
 
         uint32_t transfer_characteristics = 0;
-        if (tf == JPEGR_TF_SRGB) {
+        if (tf == ULTRAHDR_TF_SRGB) {
             transfer_characteristics = kCICPTrfnSRGB;
-        } else if (tf == JPEGR_TF_LINEAR) {
+        } else if (tf == ULTRAHDR_TF_LINEAR) {
             transfer_characteristics = kCICPTrfnLinear;
-        } else if (tf == JPEGR_TF_PQ) {
+        } else if (tf == ULTRAHDR_TF_PQ) {
             transfer_characteristics = kCICPTrfnPQ;
-        } else if (tf == JPEGR_TF_HLG) {
+        } else if (tf == ULTRAHDR_TF_HLG) {
             transfer_characteristics = kCICPTrfnHLG;
         }
         tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
     }
 
     // Compute A2B0.
-    if (tf == JPEGR_TF_PQ) {
+    if (tf == ULTRAHDR_TF_PQ) {
         std::vector<uint8_t> a2b_grid;
         a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
         size_t a2b_grid_index = 0;
@@ -520,7 +521,7 @@
     }
 
     // Compute B2A0.
-    if (tf == JPEGR_TF_PQ) {
+    if (tf == ULTRAHDR_TF_PQ) {
         auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
                                              /* has_a_curves */ false,
                                              /* grid_points */ nullptr,
@@ -541,7 +542,7 @@
 
     // Write the header.
     header.data_color_space = Endian_SwapBE32(Signature_RGB);
-    header.pcs = Endian_SwapBE32(tf == JPEGR_TF_PQ ? Signature_Lab : Signature_XYZ);
+    header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ);
     header.size = Endian_SwapBE32(profile_size);
     header.tag_count = Endian_SwapBE32(tags.size());
 
@@ -581,4 +582,4 @@
     return dataStruct;
 }
 
-} // namespace android::jpegrecoverymap
\ No newline at end of file
+} // namespace android::ultrahdr
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
similarity index 93%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/gainmapmath.h
rename to libs/ultrahdr/include/ultrahdr/gainmapmath.h
index 57fddd0..abc9356 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
-#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+#ifndef ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
+#define ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
 
 #include <cmath>
 #include <stdint.h>
 
-#include <jpegrecoverymap/jpegr.h>
+#include <ultrahdr/jpegr.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
 
@@ -132,7 +132,7 @@
 constexpr size_t kGainFactorPrecision = 10;
 constexpr size_t kGainFactorNumEntries = 1 << kGainFactorPrecision;
 struct GainLUT {
-  GainLUT(jr_metadata_ptr metadata) {
+  GainLUT(ultrahdr_metadata_ptr metadata) {
     for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
       float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
@@ -141,7 +141,7 @@
     }
   }
 
-  GainLUT(jr_metadata_ptr metadata, float displayBoost) {
+  GainLUT(ultrahdr_metadata_ptr metadata, float displayBoost) {
     float boostFactor = displayBoost > 0 ? displayBoost / metadata->maxContentBoost : 1.0f;
     for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
@@ -356,7 +356,7 @@
 /*
  * Get the conversion to apply to the HDR image for gain map generation
  */
-ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut);
+ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut);
 
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -366,16 +366,16 @@
  * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
  * luminances in linear space, and the hdr ratio to encode against.
  */
-uint8_t encodeGain(float y_sdr, float y_hdr, jr_metadata_ptr metadata);
-uint8_t encodeGain(float y_sdr, float y_hdr, jr_metadata_ptr metadata,
+uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata);
+uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
                    float log2MinContentBoost, float log2MaxContentBoost);
 
 /*
  * Calculates the linear luminance in nits after applying the given gain
  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
  */
-Color applyGain(Color e, float gain, jr_metadata_ptr metadata);
-Color applyGain(Color e, float gain, jr_metadata_ptr metadata, float displayBoost);
+Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata);
+Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost);
 Color applyGainLUT(Color e, float gain, GainLUT& gainLUT);
 
 /*
@@ -426,6 +426,6 @@
  */
 uint64_t colorToRgbaF16(Color e_gamma);
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
 
-#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+#endif // ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
similarity index 92%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
rename to libs/ultrahdr/include/ultrahdr/icc.h
index a81aa62..7f6ab88 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
+++ b/libs/ultrahdr/include/ultrahdr/icc.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_ICC_H
-#define ANDROID_JPEGRECOVERYMAP_ICC_H
+#ifndef ANDROID_ULTRAHDR_ICC_H
+#define ANDROID_ULTRAHDR_ICC_H
 
-#include <jpegrecoverymap/jpegr.h>
-#include <jpegrecoverymap/jpegrutils.h>
+#include <ultrahdr/jpegr.h>
+#include <ultrahdr/jpegrutils.h>
 #include <utils/RefBase.h>
 #include <cmath>
 #include <string>
@@ -28,7 +28,7 @@
 #define USE_BIG_ENDIAN true
 #endif
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 typedef int32_t              Fixed;
 #define Fixed1               (1 << 16)
@@ -210,12 +210,12 @@
     static constexpr size_t kNumChannels = 3;
 
     static sp<DataStruct> write_text_tag(const char* text);
-    static std::string get_desc_string(const jpegr_transfer_function tf,
-                                       const jpegr_color_gamut gamut);
+    static std::string get_desc_string(const ultrahdr_transfer_function tf,
+                                       const ultrahdr_color_gamut gamut);
     static sp<DataStruct> write_xyz_tag(float x, float y, float z);
     static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
     static sp<DataStruct> write_trc_tag_for_linear();
-    static float compute_tone_map_gain(const jpegr_transfer_function tf, float L);
+    static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
     static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
                                          uint32_t transfer_characteristics);
     static sp<DataStruct> write_mAB_or_mBA_tag(uint32_t type,
@@ -226,9 +226,9 @@
     static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
 
 public:
-    static sp<DataStruct> writeIccProfile(const jpegr_transfer_function tf,
-                                          const jpegr_color_gamut gamut);
+    static sp<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf,
+                                          const ultrahdr_color_gamut gamut);
 };
-}  // namespace android::jpegrecoverymap
+}  // namespace android::ultrahdr
 
-#endif //ANDROID_JPEGRECOVERYMAP_ICC_H
\ No newline at end of file
+#endif //ANDROID_ULTRAHDR_ICC_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
similarity index 94%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h
rename to libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
index 8748237..f642bad 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_JPEGDECODERHELPER_H
-#define ANDROID_JPEGRECOVERYMAP_JPEGDECODERHELPER_H
+#ifndef ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
+#define ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
 
 // We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
 #include <cstdio>
@@ -25,7 +25,7 @@
 }
 #include <utils/Errors.h>
 #include <vector>
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 /*
  * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
  * This class is not thread-safe.
@@ -115,6 +115,6 @@
     // Position of EXIF package, default value is -1 which means no EXIF package appears.
     size_t mExifPos;
 };
-} /* namespace android::jpegrecoverymap  */
+} /* namespace android::ultrahdr  */
 
-#endif // ANDROID_JPEGRECOVERYMAP_JPEGDECODERHELPER_H
+#endif // ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
similarity index 89%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h
rename to libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
index 8b82b2b..2c6778e 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_JPEGENCODERHELPER_H
-#define ANDROID_JPEGRECOVERYMAP_JPEGENCODERHELPER_H
+#ifndef ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
+#define ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
 
 // We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
 #include <cstdio>
@@ -28,7 +28,7 @@
 #include <utils/Errors.h>
 #include <vector>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 /*
  * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
@@ -61,6 +61,11 @@
      */
     size_t getCompressedImageSize();
 
+    /*
+     * Process 16 lines of Y and 16 lines of U/V each time.
+     * We must pass at least 16 scanlines according to libjpeg documentation.
+     */
+    static const int kCompressBatchSize = 16;
 private:
     // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
     // passed into jpeg library.
@@ -82,14 +87,11 @@
 
     // The block size for encoded jpeg image buffer.
     static const int kBlockSize = 16384;
-    // Process 16 lines of Y and 16 lines of U/V each time.
-    // We must pass at least 16 scanlines according to libjpeg documentation.
-    static const int kCompressBatchSize = 16;
 
     // The buffer that holds the compressed result.
     std::vector<JOCTET> mResultBuffer;
 };
 
-} /* namespace android::jpegrecoverymap  */
+} /* namespace android::ultrahdr  */
 
-#endif // ANDROID_JPEGRECOVERYMAP_JPEGENCODERHELPER_H
+#endif // ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
similarity index 81%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
rename to libs/ultrahdr/include/ultrahdr/jpegr.h
index ce7b33b..88038f1 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -14,41 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_JPEGR_H
-#define ANDROID_JPEGRECOVERYMAP_JPEGR_H
+#ifndef ANDROID_ULTRAHDR_JPEGR_H
+#define ANDROID_ULTRAHDR_JPEGR_H
 
 #include "jpegrerrorcode.h"
+#include "ultrahdr.h"
 
 #ifndef FLT_MAX
 #define FLT_MAX 0x1.fffffep127f
 #endif
 
-namespace android::jpegrecoverymap {
-
-// Color gamuts for image data
-typedef enum {
-  JPEGR_COLORGAMUT_UNSPECIFIED,
-  JPEGR_COLORGAMUT_BT709,
-  JPEGR_COLORGAMUT_P3,
-  JPEGR_COLORGAMUT_BT2100,
-} jpegr_color_gamut;
-
-// Transfer functions for image data
-typedef enum {
-  JPEGR_TF_UNSPECIFIED = -1,
-  JPEGR_TF_LINEAR = 0,
-  JPEGR_TF_HLG = 1,
-  JPEGR_TF_PQ = 2,
-  JPEGR_TF_SRGB = 3,
-} jpegr_transfer_function;
-
-// Target output formats for decoder
-typedef enum {
-  JPEGR_OUTPUT_SDR,          // SDR in RGBA_8888 color format
-  JPEGR_OUTPUT_HDR_LINEAR,   // HDR in F16 color format (linear)
-  JPEGR_OUTPUT_HDR_PQ,       // HDR in RGBA_1010102 color format (PQ transfer function)
-  JPEGR_OUTPUT_HDR_HLG,      // HDR in RGBA_1010102 color format (HLG transfer function)
-} jpegr_output_format;
+namespace android::ultrahdr {
 
 struct jpegr_info_struct {
     size_t width;
@@ -68,7 +44,7 @@
     // Height of the gain map or the luma plane of the image in pixels.
     int height;
     // Color gamut.
-    jpegr_color_gamut colorGamut;
+    ultrahdr_color_gamut colorGamut;
 
     // Values below are optional
     // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
@@ -96,7 +72,7 @@
     // Maximum available data length in bytes.
     int maxLength;
     // Color gamut.
-    jpegr_color_gamut colorGamut;
+    ultrahdr_color_gamut colorGamut;
 };
 
 /*
@@ -109,22 +85,9 @@
     int length;
 };
 
-/*
- * Holds information for gain map related metadata.
- */
-struct jpegr_metadata_struct {
-  // JPEG/R version
-  uint32_t version;
-  // Max Content Boost for the map
-  float maxContentBoost;
-  // Min Content Boost for the map
-  float minContentBoost;
-};
-
 typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
 typedef struct jpegr_compressed_struct* jr_compressed_ptr;
 typedef struct jpegr_exif_struct* jr_exif_ptr;
-typedef struct jpegr_metadata_struct* jr_metadata_ptr;
 typedef struct jpegr_info_struct* jr_info_ptr;
 
 class JpegR {
@@ -140,14 +103,17 @@
      * JPEG.
      * @param uncompressed_p010_image uncompressed HDR image in P010 color format
      * @param hdr_tf transfer function of the HDR image
-     * @param dest destination of the compressed JPEGR image
+     * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+     *             represents the maximum available size of the desitination buffer, and it must be
+     *             set before calling this method. If the encoded JPEGR size exceeds
+     *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
      * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
      *                the highest quality
      * @param exif pointer to the exif metadata.
      * @return NO_ERROR if encoding succeeds, error code if error occurs.
      */
     status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                         jpegr_transfer_function hdr_tf,
+                         ultrahdr_transfer_function hdr_tf,
                          jr_compressed_ptr dest,
                          int quality,
                          jr_exif_ptr exif);
@@ -162,7 +128,10 @@
      * @param uncompressed_p010_image uncompressed HDR image in P010 color format
      * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
      * @param hdr_tf transfer function of the HDR image
-     * @param dest destination of the compressed JPEGR image
+     * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+     *             represents the maximum available size of the desitination buffer, and it must be
+     *             set before calling this method. If the encoded JPEGR size exceeds
+     *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
      * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
      *                the highest quality
      * @param exif pointer to the exif metadata.
@@ -170,7 +139,7 @@
      */
     status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                          jr_uncompressed_ptr uncompressed_yuv_420_image,
-                         jpegr_transfer_function hdr_tf,
+                         ultrahdr_transfer_function hdr_tf,
                          jr_compressed_ptr dest,
                          int quality,
                          jr_exif_ptr exif);
@@ -189,13 +158,16 @@
      *                                         input
      * @param compressed_jpeg_image compressed 8-bit JPEG image
      * @param hdr_tf transfer function of the HDR image
-     * @param dest destination of the compressed JPEGR image
+     * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+     *             represents the maximum available size of the desitination buffer, and it must be
+     *             set before calling this method. If the encoded JPEGR size exceeds
+     *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
      * @return NO_ERROR if encoding succeeds, error code if error occurs.
      */
     status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                          jr_uncompressed_ptr uncompressed_yuv_420_image,
                          jr_compressed_ptr compressed_jpeg_image,
-                         jpegr_transfer_function hdr_tf,
+                         ultrahdr_transfer_function hdr_tf,
                          jr_compressed_ptr dest);
 
     /*
@@ -210,12 +182,34 @@
      * @param uncompressed_p010_image uncompressed HDR image in P010 color format
      * @param compressed_jpeg_image compressed 8-bit JPEG image
      * @param hdr_tf transfer function of the HDR image
-     * @param dest destination of the compressed JPEGR image
+     * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+     *             represents the maximum available size of the desitination buffer, and it must be
+     *             set before calling this method. If the encoded JPEGR size exceeds
+     *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
      * @return NO_ERROR if encoding succeeds, error code if error occurs.
      */
     status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                          jr_compressed_ptr compressed_jpeg_image,
-                         jpegr_transfer_function hdr_tf,
+                         ultrahdr_transfer_function hdr_tf,
+                         jr_compressed_ptr dest);
+
+    /*
+     * Encode API-4
+     * Assemble JPEGR image from SDR JPEG and gainmap JPEG.
+     *
+     * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format.
+     * @param compressed_jpeg_image compressed 8-bit JPEG image
+     * @param compressed_gainmap compressed 8-bit JPEG single channel image
+     * @param metadata metadata to be written in XMP of the primary jpeg
+     * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+     *             represents the maximum available size of the desitination buffer, and it must be
+     *             set before calling this method. If the encoded JPEGR size exceeds
+     *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+     * @return NO_ERROR if encoding succeeds, error code if error occurs.
+     */
+    status_t encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
+                         jr_compressed_ptr compressed_gainmap,
+                         ultrahdr_metadata_ptr metadata,
                          jr_compressed_ptr dest);
 
     /*
@@ -249,16 +243,16 @@
      * @param metadata destination of the decoded metadata. The default value is NULL where the
                        decoder will do nothing about it. If configured not NULL the decoder will
                        write metadata into this structure. the format of metadata is defined in
-                       {@code jpegr_metadata}.
+                       {@code ultrahdr_metadata_struct}.
      * @return NO_ERROR if decoding succeeds, error code if error occurs.
      */
     status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                          jr_uncompressed_ptr dest,
                          float max_display_boost = FLT_MAX,
                          jr_exif_ptr exif = nullptr,
-                         jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR,
+                         ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
                          jr_uncompressed_ptr gain_map = nullptr,
-                         jr_metadata_ptr metadata = nullptr);
+                         ultrahdr_metadata_ptr metadata = nullptr);
 
     /*
     * Gets Info from JPEGR file without decoding it.
@@ -286,8 +280,8 @@
      */
     status_t generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                              jr_uncompressed_ptr uncompressed_p010_image,
-                             jpegr_transfer_function hdr_tf,
-                             jr_metadata_ptr metadata,
+                             ultrahdr_transfer_function hdr_tf,
+                             ultrahdr_metadata_ptr metadata,
                              jr_uncompressed_ptr dest);
 
     /*
@@ -308,8 +302,8 @@
      */
     status_t applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                           jr_uncompressed_ptr uncompressed_gain_map,
-                          jr_metadata_ptr metadata,
-                          jpegr_output_format output_format,
+                          ultrahdr_metadata_ptr metadata,
+                          ultrahdr_output_format output_format,
                           float max_display_boost,
                           jr_uncompressed_ptr dest);
 
@@ -365,7 +359,7 @@
     status_t appendGainMap(jr_compressed_ptr compressed_jpeg_image,
                            jr_compressed_ptr compressed_gain_map,
                            jr_exif_ptr exif,
-                           jr_metadata_ptr metadata,
+                           ultrahdr_metadata_ptr metadata,
                            jr_compressed_ptr dest);
 
     /*
@@ -389,6 +383,6 @@
                                  jr_uncompressed_ptr uncompressed_yuv_420_image);
 };
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
 
-#endif // ANDROID_JPEGRECOVERYMAP_JPEGR_H
+#endif // ANDROID_ULTRAHDR_JPEGR_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
similarity index 91%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
rename to libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
index 159aaa8..9f59c3e 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_ULTRAHDR_JPEGRERRORCODE_H
+#define ANDROID_ULTRAHDR_JPEGRERRORCODE_H
+
 #include <utils/Errors.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 enum {
     // status_t map for errors in the media framework
@@ -50,4 +53,6 @@
     ERROR_JPEGR_UNSUPPORTED_FEATURE     = -20000,
 };
 
-}  // namespace android::jpegrecoverymap
+}  // namespace android::ultrahdr
+
+#endif // ANDROID_ULTRAHDR_JPEGRERRORCODE_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/ultrahdr/include/ultrahdr/jpegrutils.h
similarity index 88%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
rename to libs/ultrahdr/include/ultrahdr/jpegrutils.h
index 09f4165..4ab664e 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrutils.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
-#define ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
+#ifndef ANDROID_ULTRAHDR_JPEGRUTILS_H
+#define ANDROID_ULTRAHDR_JPEGRUTILS_H
 
-#include <jpegrecoverymap/jpegr.h>
+#include <ultrahdr/jpegr.h>
 #include <utils/RefBase.h>
 
 #include <sstream>
@@ -25,7 +25,7 @@
 #include <string>
 #include <cstdio>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 static constexpr uint32_t EndianSwap32(uint32_t value) {
     return ((value & 0xFF) << 24) |
@@ -45,7 +45,7 @@
     #define Endian_SwapBE16(n) (n)
 #endif
 
-struct jpegr_metadata_struct;
+struct ultrahdr_metadata_struct;
 /*
  * Mutable data structure. Holds information for metadata.
  */
@@ -87,7 +87,7 @@
  * @param metadata place to store HDR metadata values
  * @return true if metadata is successfully retrieved, false otherwise
 */
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata);
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata);
 
 /*
  * This method generates XMP metadata for the primary image.
@@ -102,7 +102,9 @@
  *     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  *     <rdf:Description
  *       xmlns:Container="http://ns.google.com/photos/1.0/container/"
- *       xmlns:Item="http://ns.google.com/photos/1.0/container/item/">
+ *       xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
+ *       xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
+ *       hdrgm:Version="1">
  *       <Container:Directory>
  *         <rdf:Seq>
  *           <rdf:li
@@ -127,7 +129,8 @@
  * @param secondary_image_length length of secondary image
  * @return XMP metadata in type of string
  */
-std::string generateXmpForPrimaryImage(int secondary_image_length);
+std::string generateXmpForPrimaryImage(int secondary_image_length,
+                                       ultrahdr_metadata_struct& metadata);
 
 /*
  * This method generates XMP metadata for the recovery map image.
@@ -158,7 +161,7 @@
  * @param metadata JPEG/R metadata to encode as XMP
  * @return XMP metadata in type of string
  */
- std::string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata);
-}  // namespace android::jpegrecoverymap
+ std::string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata);
+}  // namespace android::ultrahdr
 
-#endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
+#endif //ANDROID_ULTRAHDR_JPEGRUTILS_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h b/libs/ultrahdr/include/ultrahdr/multipictureformat.h
similarity index 87%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
rename to libs/ultrahdr/include/ultrahdr/multipictureformat.h
index cf3387d..c5bd09d 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
+++ b/libs/ultrahdr/include/ultrahdr/multipictureformat.h
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
-#define ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+#ifndef ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
+#define ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
 
-#include <jpegrecoverymap/jpegrutils.h>
+#include <ultrahdr/jpegrutils.h>
 
 #ifdef USE_BIG_ENDIAN
 #undef USE_BIG_ENDIAN
 #define USE_BIG_ENDIAN true
 #endif
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 constexpr size_t kNumPictures = 2;
 constexpr size_t kMpEndianSize = 4;
@@ -59,6 +59,6 @@
 sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
                            int secondary_image_size, int secondary_image_offset);
 
-}  // namespace android::jpegrecoverymap
+}  // namespace android::ultrahdr
 
-#endif //ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+#endif //ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
new file mode 100644
index 0000000..f970936
--- /dev/null
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 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 ANDROID_ULTRAHDR_ULTRAHDR_H
+#define ANDROID_ULTRAHDR_ULTRAHDR_H
+
+namespace android::ultrahdr {
+// Color gamuts for image data
+typedef enum {
+  ULTRAHDR_COLORGAMUT_UNSPECIFIED,
+  ULTRAHDR_COLORGAMUT_BT709,
+  ULTRAHDR_COLORGAMUT_P3,
+  ULTRAHDR_COLORGAMUT_BT2100,
+} ultrahdr_color_gamut;
+
+// Transfer functions for image data
+typedef enum {
+  ULTRAHDR_TF_UNSPECIFIED = -1,
+  ULTRAHDR_TF_LINEAR = 0,
+  ULTRAHDR_TF_HLG = 1,
+  ULTRAHDR_TF_PQ = 2,
+  ULTRAHDR_TF_SRGB = 3,
+} ultrahdr_transfer_function;
+
+// Target output formats for decoder
+typedef enum {
+  ULTRAHDR_OUTPUT_SDR,          // SDR in RGBA_8888 color format
+  ULTRAHDR_OUTPUT_HDR_LINEAR,   // HDR in F16 color format (linear)
+  ULTRAHDR_OUTPUT_HDR_PQ,       // HDR in RGBA_1010102 color format (PQ transfer function)
+  ULTRAHDR_OUTPUT_HDR_HLG,      // HDR in RGBA_1010102 color format (HLG transfer function)
+} ultrahdr_output_format;
+
+/*
+ * Holds information for gain map related metadata.
+ */
+struct ultrahdr_metadata_struct {
+  // Ultra HDR library version
+  const char* version;
+  // Max Content Boost for the map
+  float maxContentBoost;
+  // Min Content Boost for the map
+  float minContentBoost;
+};
+typedef struct ultrahdr_metadata_struct* ultrahdr_metadata_ptr;
+
+}  // namespace android::ultrahdr
+
+#endif //ANDROID_ULTRAHDR_ULTRAHDR_H
diff --git a/libs/jpegrecoverymap/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
similarity index 98%
rename from libs/jpegrecoverymap/jpegdecoderhelper.cpp
rename to libs/ultrahdr/jpegdecoderhelper.cpp
index d36bbf8..12217b7 100644
--- a/libs/jpegrecoverymap/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegdecoderhelper.h>
+#include <ultrahdr/jpegdecoderhelper.h>
 
 #include <utils/Log.h>
 
@@ -24,7 +24,7 @@
 
 using namespace std;
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 const uint32_t kAPP0Marker = JPEG_APP0;      // JFIF
 const uint32_t kAPP1Marker = JPEG_APP0 + 1;  // EXIF, XMP
@@ -413,4 +413,4 @@
     return true;
 }
 
-} // namespace jpegrecoverymap
+} // namespace ultrahdr
diff --git a/libs/jpegrecoverymap/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp
similarity index 96%
rename from libs/jpegrecoverymap/jpegencoderhelper.cpp
rename to libs/ultrahdr/jpegencoderhelper.cpp
index 586cd34..10a7630 100644
--- a/libs/jpegrecoverymap/jpegencoderhelper.cpp
+++ b/libs/ultrahdr/jpegencoderhelper.cpp
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegencoderhelper.h>
+#include <ultrahdr/jpegencoderhelper.h>
 
 #include <utils/Log.h>
 
 #include <errno.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 // The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
 struct destination_mgr {
@@ -38,11 +38,6 @@
 bool JpegEncoderHelper::compressImage(const void* image, int width, int height, int quality,
                                    const void* iccBuffer, unsigned int iccSize,
                                    bool isSingleChannel) {
-    if (width % 8 != 0 || height % 2 != 0) {
-        ALOGE("Image size can not be handled: %dx%d", width, height);
-        return false;
-    }
-
     mResultBuffer.clear();
     if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) {
         return false;
@@ -236,4 +231,4 @@
     return true;
 }
 
-} // namespace jpegrecoverymap
+} // namespace ultrahdr
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
similarity index 90%
rename from libs/jpegrecoverymap/jpegr.cpp
rename to libs/ultrahdr/jpegr.cpp
index 2590f636..0f7aa27 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegr.h>
-#include <jpegrecoverymap/jpegencoderhelper.h>
-#include <jpegrecoverymap/jpegdecoderhelper.h>
-#include <jpegrecoverymap/gainmapmath.h>
-#include <jpegrecoverymap/jpegrutils.h>
-#include <jpegrecoverymap/multipictureformat.h>
-#include <jpegrecoverymap/icc.h>
+#include <ultrahdr/jpegr.h>
+#include <ultrahdr/jpegencoderhelper.h>
+#include <ultrahdr/jpegdecoderhelper.h>
+#include <ultrahdr/gainmapmath.h>
+#include <ultrahdr/jpegrutils.h>
+#include <ultrahdr/multipictureformat.h>
+#include <ultrahdr/icc.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/jpeg/jpeg_info.h>
@@ -43,7 +43,7 @@
 using namespace std;
 using namespace photos_editing_formats::image_io;
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 #define USE_SRGB_INVOETF_LUT 1
 #define USE_HLG_OETF_LUT 1
@@ -61,14 +61,17 @@
   }
 
 // The current JPEGR version that we encode to
-static const uint32_t kJpegrVersion = 1;
+static const char* const kJpegrVersion = "1.0";
 
 // Map is quarter res / sixteenth size
 static const size_t kMapDimensionScaleFactor = 4;
 // JPEG block size.
-// JPEG encoding / decoding will require 8 x 8 DCT transform.
-// Width must be 8 dividable, and height must be 2 dividable.
-static const size_t kJpegBlock = 8;
+// JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
+// and 8 x 8 for chroma.
+// Width must be 16 dividable for luma, and 8 dividable for chroma.
+// If this criteria is not ficilitated, we will pad zeros based on the required block size.
+static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
+static const size_t kJpegBlockSquare = kJpegBlock * kJpegBlock;
 // JPEG compress quality (0 ~ 100) for gain map
 static const int kMapCompressQuality = 85;
 
@@ -92,13 +95,6 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  if (uncompressed_p010_image->width % kJpegBlock != 0
-          || uncompressed_p010_image->height % 2 != 0) {
-    ALOGE("Image size can not be handled: %dx%d.",
-            uncompressed_p010_image->width, uncompressed_p010_image->height);
-    return ERROR_JPEGR_INVALID_INPUT_TYPE;
-  }
-
   if (uncompressed_p010_image->luma_stride != 0
           && uncompressed_p010_image->luma_stride < uncompressed_p010_image->width) {
     ALOGE("Image stride can not be smaller than width, stride=%d, width=%d",
@@ -136,7 +132,7 @@
 
 /* Encode API-0 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                            jpegr_transfer_function hdr_tf,
+                            ultrahdr_transfer_function hdr_tf,
                             jr_compressed_ptr dest,
                             int quality,
                             jr_exif_ptr exif) {
@@ -153,12 +149,17 @@
     return ret;
   }
 
-  jpegr_metadata_struct metadata;
+  ultrahdr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct uncompressed_yuv_420_image;
-  unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
-      uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
+  size_t gain_map_length = uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2;
+  // Pad a pseudo chroma block (kJpegBlock / 2) x (kJpegBlock / 2)
+  // if width is not kJpegBlock aligned.
+  if (uncompressed_p010_image->width % kJpegBlock != 0) {
+    gain_map_length += kJpegBlockSquare / 4;
+  }
+  unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(gain_map_length);
   uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
   JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
 
@@ -174,7 +175,7 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressGainMap(&map, &compressed_map));
 
-  sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+  sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
                                                   uncompressed_yuv_420_image.colorGamut);
 
   JpegEncoderHelper jpeg_encoder;
@@ -196,7 +197,7 @@
 /* Encode API-1 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                             jr_uncompressed_ptr uncompressed_yuv_420_image,
-                            jpegr_transfer_function hdr_tf,
+                            ultrahdr_transfer_function hdr_tf,
                             jr_compressed_ptr dest,
                             int quality,
                             jr_exif_ptr exif) {
@@ -215,7 +216,7 @@
     return ret;
   }
 
-  jpegr_metadata_struct metadata;
+  ultrahdr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -230,7 +231,7 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressGainMap(&map, &compressed_map));
 
-  sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+  sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
                                                   uncompressed_yuv_420_image->colorGamut);
 
   JpegEncoderHelper jpeg_encoder;
@@ -253,7 +254,7 @@
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                             jr_uncompressed_ptr uncompressed_yuv_420_image,
                             jr_compressed_ptr compressed_jpeg_image,
-                            jpegr_transfer_function hdr_tf,
+                            ultrahdr_transfer_function hdr_tf,
                             jr_compressed_ptr dest) {
   if (uncompressed_p010_image == nullptr
    || uncompressed_yuv_420_image == nullptr
@@ -267,7 +268,7 @@
     return ret;
   }
 
-  jpegr_metadata_struct metadata;
+  ultrahdr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -290,7 +291,7 @@
 /* Encode API-3 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                             jr_compressed_ptr compressed_jpeg_image,
-                            jpegr_transfer_function hdr_tf,
+                            ultrahdr_transfer_function hdr_tf,
                             jr_compressed_ptr dest) {
   if (uncompressed_p010_image == nullptr
    || compressed_jpeg_image == nullptr
@@ -318,7 +319,7 @@
     return ERROR_JPEGR_RESOLUTION_MISMATCH;
   }
 
-  jpegr_metadata_struct metadata;
+  ultrahdr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -338,6 +339,16 @@
   return NO_ERROR;
 }
 
+/* Encode API-4 */
+status_t JpegR::encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
+                            jr_compressed_ptr compressed_gainmap,
+                            ultrahdr_metadata_ptr metadata,
+                            jr_compressed_ptr dest) {
+  JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
+          metadata, dest));
+  return NO_ERROR;
+}
+
 status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
   if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -362,9 +373,9 @@
                             jr_uncompressed_ptr dest,
                             float max_display_boost,
                             jr_exif_ptr exif,
-                            jpegr_output_format output_format,
+                            ultrahdr_output_format output_format,
                             jr_uncompressed_ptr gain_map,
-                            jr_metadata_ptr metadata) {
+                            ultrahdr_metadata_ptr metadata) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -373,7 +384,7 @@
       return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  if (output_format == JPEGR_OUTPUT_SDR) {
+  if (output_format == ULTRAHDR_OUTPUT_SDR) {
     JpegDecoderHelper jpeg_decoder;
     if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
                                       true)) {
@@ -423,19 +434,19 @@
     memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size);
   }
 
-  jpegr_metadata_struct jr_metadata;
+  ultrahdr_metadata_struct uhdr_metadata;
   if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
-                          gain_map_decoder.getXMPSize(), &jr_metadata)) {
+                          gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
   if (metadata != nullptr) {
-      metadata->version = jr_metadata.version;
-      metadata->minContentBoost = jr_metadata.minContentBoost;
-      metadata->maxContentBoost = jr_metadata.maxContentBoost;
+      metadata->version = uhdr_metadata.version;
+      metadata->minContentBoost = uhdr_metadata.minContentBoost;
+      metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
   }
 
-  if (output_format == JPEGR_OUTPUT_SDR) {
+  if (output_format == ULTRAHDR_OUTPUT_SDR) {
     return NO_ERROR;
   }
 
@@ -465,7 +476,7 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &jr_metadata, output_format,
+  JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format,
                            max_display_boost, dest));
   return NO_ERROR;
 }
@@ -493,7 +504,7 @@
 
   memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
   dest->length = jpeg_encoder.getCompressedImageSize();
-  dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
+  dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
 
   return NO_ERROR;
 }
@@ -556,8 +567,8 @@
 
 status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                 jr_uncompressed_ptr uncompressed_p010_image,
-                                jpegr_transfer_function hdr_tf,
-                                jr_metadata_ptr metadata,
+                                ultrahdr_transfer_function hdr_tf,
+                                ultrahdr_metadata_ptr metadata,
                                 jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_p010_image == nullptr
@@ -571,8 +582,8 @@
     return ERROR_JPEGR_RESOLUTION_MISMATCH;
   }
 
-  if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
-   || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
+  if (uncompressed_yuv_420_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED
+   || uncompressed_p010_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
     return ERROR_JPEGR_INVALID_COLORGAMUT;
   }
 
@@ -586,7 +597,7 @@
 
   dest->width = map_stride;
   dest->height = map_height_aligned;
-  dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
+  dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
   dest->data = new uint8_t[map_stride * map_height_aligned];
   std::unique_ptr<uint8_t[]> map_data;
   map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
@@ -594,10 +605,10 @@
   ColorTransformFn hdrInvOetf = nullptr;
   float hdr_white_nits = 0.0f;
   switch (hdr_tf) {
-    case JPEGR_TF_LINEAR:
+    case ULTRAHDR_TF_LINEAR:
       hdrInvOetf = identityConversion;
       break;
-    case JPEGR_TF_HLG:
+    case ULTRAHDR_TF_HLG:
 #if USE_HLG_INVOETF_LUT
       hdrInvOetf = hlgInvOetfLUT;
 #else
@@ -605,7 +616,7 @@
 #endif
       hdr_white_nits = kHlgMaxNits;
       break;
-    case JPEGR_TF_PQ:
+    case ULTRAHDR_TF_PQ:
 #if USE_PQ_INVOETF_LUT
       hdrInvOetf = pqInvOetfLUT;
 #else
@@ -628,16 +639,16 @@
 
   ColorCalculationFn luminanceFn = nullptr;
   switch (uncompressed_yuv_420_image->colorGamut) {
-    case JPEGR_COLORGAMUT_BT709:
+    case ULTRAHDR_COLORGAMUT_BT709:
       luminanceFn = srgbLuminance;
       break;
-    case JPEGR_COLORGAMUT_P3:
+    case ULTRAHDR_COLORGAMUT_P3:
       luminanceFn = p3Luminance;
       break;
-    case JPEGR_COLORGAMUT_BT2100:
+    case ULTRAHDR_COLORGAMUT_BT2100:
       luminanceFn = bt2100Luminance;
       break;
-    case JPEGR_COLORGAMUT_UNSPECIFIED:
+    case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
       // Should be impossible to hit after input validation.
       return ERROR_JPEGR_INVALID_COLORGAMUT;
   }
@@ -703,8 +714,8 @@
 
 status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                              jr_uncompressed_ptr uncompressed_gain_map,
-                             jr_metadata_ptr metadata,
-                             jpegr_output_format output_format,
+                             ultrahdr_metadata_ptr metadata,
+                             ultrahdr_output_format output_format,
                              float max_display_boost,
                              jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
@@ -759,13 +770,13 @@
           size_t pixel_idx = x + y * width;
 
           switch (output_format) {
-            case JPEGR_OUTPUT_HDR_LINEAR:
+            case ULTRAHDR_OUTPUT_HDR_LINEAR:
             {
               uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
               reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
               break;
             }
-            case JPEGR_OUTPUT_HDR_HLG:
+            case ULTRAHDR_OUTPUT_HDR_HLG:
             {
 #if USE_HLG_OETF_LUT
               ColorTransformFn hdrOetf = hlgOetfLUT;
@@ -777,7 +788,7 @@
               reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
               break;
             }
-            case JPEGR_OUTPUT_HDR_PQ:
+            case ULTRAHDR_OUTPUT_HDR_PQ:
             {
 #if USE_HLG_OETF_LUT
               ColorTransformFn hdrOetf = pqOetfLUT;
@@ -910,7 +921,7 @@
 status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
                               jr_compressed_ptr compressed_gain_map,
                               jr_exif_ptr exif,
-                              jr_metadata_ptr metadata,
+                              ultrahdr_metadata_ptr metadata,
                               jr_compressed_ptr dest) {
   if (compressed_jpeg_image == nullptr
    || compressed_gain_map == nullptr
@@ -932,7 +943,7 @@
                                  + xmp_secondary_length
                                  + compressed_gain_map->length;
   // primary image
-  const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size);
+  const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
   // same as primary
   const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
 
@@ -1034,7 +1045,7 @@
   uint16_t* src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
 
   if (src_chroma_data == nullptr) {
-    src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src->luma_stride * src->height];
+    src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src_luma_stride * src->height];
   }
   if (src_luma_stride == 0) {
     src_luma_stride = src->width;
@@ -1077,4 +1088,4 @@
   return NO_ERROR;
 }
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/ultrahdr/jpegrutils.cpp
similarity index 95%
rename from libs/jpegrecoverymap/jpegrutils.cpp
rename to libs/ultrahdr/jpegrutils.cpp
index cde0ceb..6430af1 100644
--- a/libs/jpegrecoverymap/jpegrutils.cpp
+++ b/libs/ultrahdr/jpegrutils.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegrutils.h>
+#include <ultrahdr/jpegrutils.h>
 
 #include <algorithm>
 #include <cmath>
@@ -30,7 +30,7 @@
 using namespace photos_editing_formats::image_io;
 using namespace std;
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 /*
  * Helper function used for generating XMP metadata.
  *
@@ -256,7 +256,7 @@
 const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
 const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
 
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata) {
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
     string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
 
     if (xmp_size < nameSpace.size()+2) {
@@ -302,7 +302,7 @@
     return true;
 }
 
-string generateXmpForPrimaryImage(int secondary_image_length) {
+string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
   const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
   const vector<string> kLiItem({string("rdf:li"), kConItem});
 
@@ -316,6 +316,8 @@
   writer.StartWritingElement("rdf:Description");
   writer.WriteXmlns(kContainerPrefix, kContainerUri);
   writer.WriteXmlns(kItemPrefix, kItemUri);
+  writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
+  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
 
   writer.StartWritingElements(kConDirSeq);
 
@@ -338,7 +340,7 @@
   return ss.str();
 }
 
-string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata) {
+string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata) {
   const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
 
   std::stringstream ss;
@@ -365,4 +367,4 @@
   return ss.str();
 }
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
diff --git a/libs/jpegrecoverymap/multipictureformat.cpp b/libs/ultrahdr/multipictureformat.cpp
similarity index 95%
rename from libs/jpegrecoverymap/multipictureformat.cpp
rename to libs/ultrahdr/multipictureformat.cpp
index a219aef..7a265c6 100644
--- a/libs/jpegrecoverymap/multipictureformat.cpp
+++ b/libs/ultrahdr/multipictureformat.cpp
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <jpegrecoverymap/multipictureformat.h>
-#include <jpegrecoverymap/jpegrutils.h>
+#include <ultrahdr/multipictureformat.h>
+#include <ultrahdr/jpegrutils.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 size_t calculateMpfSize() {
     return sizeof(kMpfSig) +                 // Signature
             kMpEndianSize +                   // Endianness
@@ -91,4 +91,4 @@
     return dataStruct;
 }
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
similarity index 96%
rename from libs/jpegrecoverymap/tests/Android.bp
rename to libs/ultrahdr/tests/Android.bp
index 59b1237..7dd9d04 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,7 +22,7 @@
 }
 
 cc_test {
-    name: "libjpegrecoverymap_test",
+    name: "libultrahdr_test",
     test_suites: ["device-tests"],
     srcs: [
         "jpegr_test.cpp",
@@ -38,7 +38,7 @@
         "libgtest",
         "libjpegdecoder",
         "libjpegencoder",
-        "libjpegrecoverymap",
+        "libultrahdr",
         "libutils",
     ],
 }
diff --git a/libs/jpegrecoverymap/tests/data/jpeg_image.jpg b/libs/ultrahdr/tests/data/jpeg_image.jpg
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/jpeg_image.jpg
rename to libs/ultrahdr/tests/data/jpeg_image.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 b/libs/ultrahdr/tests/data/minnie-318x240.yu12
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/minnie-318x240.yu12
rename to libs/ultrahdr/tests/data/minnie-318x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg b/libs/ultrahdr/tests/data/minnie-320x240-y.jpg
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg
rename to libs/ultrahdr/tests/data/minnie-320x240-y.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg b/libs/ultrahdr/tests/data/minnie-320x240-yuv.jpg
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg
rename to libs/ultrahdr/tests/data/minnie-320x240-yuv.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.y b/libs/ultrahdr/tests/data/minnie-320x240.y
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/minnie-320x240.y
rename to libs/ultrahdr/tests/data/minnie-320x240.y
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 b/libs/ultrahdr/tests/data/minnie-320x240.yu12
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/minnie-320x240.yu12
rename to libs/ultrahdr/tests/data/minnie-320x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 b/libs/ultrahdr/tests/data/raw_p010_image.p010
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/raw_p010_image.p010
rename to libs/ultrahdr/tests/data/raw_p010_image.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image_with_stride.p010 b/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/raw_p010_image_with_stride.p010
rename to libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420 b/libs/ultrahdr/tests/data/raw_yuv420_image.yuv420
similarity index 100%
rename from libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
rename to libs/ultrahdr/tests/data/raw_yuv420_image.yuv420
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/gainmapmath_test.cpp b/libs/ultrahdr/tests/gainmapmath_test.cpp
similarity index 94%
rename from libs/jpegrecoverymap/tests/gainmapmath_test.cpp
rename to libs/ultrahdr/tests/gainmapmath_test.cpp
index 21de2e6..c456653 100644
--- a/libs/jpegrecoverymap/tests/gainmapmath_test.cpp
+++ b/libs/ultrahdr/tests/gainmapmath_test.cpp
@@ -17,9 +17,9 @@
 #include <cmath>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
-#include <jpegrecoverymap/gainmapmath.h>
+#include <ultrahdr/gainmapmath.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 class GainMapMathTest : public testing::Test {
 public:
@@ -88,7 +88,7 @@
     return luminance_scaled * scale_factor;
   }
 
-  Color Recover(Color yuv_gamma, float gain, jr_metadata_ptr metadata) {
+  Color Recover(Color yuv_gamma, float gain, ultrahdr_metadata_ptr metadata) {
     Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
     Color rgb = srgbInvOetf(rgb_gamma);
     return applyGain(rgb, gain, metadata);
@@ -108,7 +108,7 @@
       0xB0, 0xB1,
       0xB2, 0xB3,
     };
-    return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
   }
 
   Color (*Yuv420Colors())[4] {
@@ -141,7 +141,7 @@
       0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
       0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
     };
-    return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
   }
 
   Color (*P010Colors())[4] {
@@ -170,7 +170,7 @@
       0x02, 0x12, 0x22, 0x32,
       0x03, 0x13, 0x23, 0x33,
     };
-    return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED };
+    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_UNSPECIFIED };
   }
 
   float (*MapValues())[4] {
@@ -554,7 +554,7 @@
 
 TEST_F(GainMapMathTest, applyGainLUT) {
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+    ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
                                        .minContentBoost = 1.0f / static_cast<float>(boost) };
     GainLUT gainLUT(&metadata);
     GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
@@ -584,7 +584,7 @@
   }
 
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+    ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
                                        .minContentBoost = 1.0f };
     GainLUT gainLUT(&metadata);
     GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
@@ -614,7 +614,7 @@
   }
 
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+    ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
                                        .minContentBoost = 1.0f / pow(static_cast<float>(boost),
                                                               1.0f / 3.0f) };
     GainLUT gainLUT(&metadata);
@@ -654,45 +654,45 @@
 }
 
 TEST_F(GainMapMathTest, ColorConversionLookup) {
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT709),
             identityConversion);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3),
             p3ToBt709);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT2100),
             bt2100ToBt709);
 
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT709),
             bt709ToP3);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_P3),
             identityConversion);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100),
             bt2100ToP3);
 
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT709),
             bt709ToBt2100);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_P3),
             p3ToBt2100);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT2100),
             identityConversion);
 
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT709),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_P3),
             nullptr);
-  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100),
+  EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT2100),
             nullptr);
 }
 
 TEST_F(GainMapMathTest, EncodeGain) {
-  jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+  ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
                                      .minContentBoost = 1.0f / 4.0f };
 
   EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127);
@@ -750,7 +750,7 @@
 }
 
 TEST_F(GainMapMathTest, ApplyGain) {
-  jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+  ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
                                      .minContentBoost = 1.0f / 4.0f };
   float displayBoost = metadata.maxContentBoost;
 
@@ -1049,7 +1049,7 @@
 }
 
 TEST_F(GainMapMathTest, ApplyMap) {
-  jpegr_metadata_struct metadata = { .maxContentBoost = 8.0f,
+  ultrahdr_metadata_struct metadata = { .maxContentBoost = 8.0f,
                                      .minContentBoost = 1.0f / 8.0f };
 
   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
@@ -1134,4 +1134,4 @@
                 RgbWhite() / 2.0f);
 }
 
-} // namespace android::jpegrecoverymap
+} // namespace android::ultrahdr
diff --git a/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
similarity index 95%
rename from libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp
rename to libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
index 2f32a56..c79dbe3 100644
--- a/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegdecoderhelper.h>
+#include <ultrahdr/jpegdecoderhelper.h>
 #include <gtest/gtest.h>
 #include <utils/Log.h>
 
 #include <fcntl.h>
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 #define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
 #define YUV_IMAGE_SIZE 20193
@@ -99,4 +99,4 @@
     ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
 }
 
-}  // namespace android::jpegrecoverymap
\ No newline at end of file
+}  // namespace android::ultrahdr
\ No newline at end of file
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
new file mode 100644
index 0000000..8f18ac0
--- /dev/null
+++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2022 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 <ultrahdr/jpegencoderhelper.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <fcntl.h>
+
+namespace android::ultrahdr {
+
+#define ALIGNED_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
+#define ALIGNED_IMAGE_WIDTH 320
+#define ALIGNED_IMAGE_HEIGHT 240
+#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y"
+#define SINGLE_CHANNEL_IMAGE_WIDTH ALIGNED_IMAGE_WIDTH
+#define SINGLE_CHANNEL_IMAGE_HEIGHT ALIGNED_IMAGE_HEIGHT
+#define UNALIGNED_IMAGE "/sdcard/Documents/minnie-318x240.yu12"
+#define UNALIGNED_IMAGE_WIDTH 318
+#define UNALIGNED_IMAGE_HEIGHT 240
+#define JPEG_QUALITY 90
+
+class JpegEncoderHelperTest : public testing::Test {
+public:
+    struct Image {
+        std::unique_ptr<uint8_t[]> buffer;
+        size_t width;
+        size_t height;
+    };
+    JpegEncoderHelperTest();
+    ~JpegEncoderHelperTest();
+protected:
+    virtual void SetUp();
+    virtual void TearDown();
+
+    Image mAlignedImage, mUnalignedImage, mSingleChannelImage;
+};
+
+JpegEncoderHelperTest::JpegEncoderHelperTest() {}
+
+JpegEncoderHelperTest::~JpegEncoderHelperTest() {}
+
+static size_t getFileSize(int fd) {
+    struct stat st;
+    if (fstat(fd, &st) < 0) {
+        ALOGW("%s : fstat failed", __func__);
+        return 0;
+    }
+    return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], JpegEncoderHelperTest::Image* result) {
+    int fd = open(filename, O_CLOEXEC);
+    if (fd < 0) {
+        return false;
+    }
+    int length = getFileSize(fd);
+    if (length == 0) {
+        close(fd);
+        return false;
+    }
+    result->buffer.reset(new uint8_t[length]);
+    if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
+        close(fd);
+        return false;
+    }
+    close(fd);
+    return true;
+}
+
+void JpegEncoderHelperTest::SetUp() {
+    if (!loadFile(ALIGNED_IMAGE, &mAlignedImage)) {
+        FAIL() << "Load file " << ALIGNED_IMAGE << " failed";
+    }
+    mAlignedImage.width = ALIGNED_IMAGE_WIDTH;
+    mAlignedImage.height = ALIGNED_IMAGE_HEIGHT;
+    if (!loadFile(UNALIGNED_IMAGE, &mUnalignedImage)) {
+        FAIL() << "Load file " << UNALIGNED_IMAGE << " failed";
+    }
+    mUnalignedImage.width = UNALIGNED_IMAGE_WIDTH;
+    mUnalignedImage.height = UNALIGNED_IMAGE_HEIGHT;
+    if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
+        FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
+    }
+    mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
+    mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
+}
+
+void JpegEncoderHelperTest::TearDown() {}
+
+TEST_F(JpegEncoderHelperTest, encodeAlignedImage) {
+    JpegEncoderHelper encoder;
+    EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(), mAlignedImage.width,
+                                      mAlignedImage.height, JPEG_QUALITY, NULL, 0));
+    ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+// The width of the "unaligned" image is not 16-aligned, and will fail if encoded directly.
+// Should pass with the padding zero method.
+TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
+    JpegEncoderHelper encoder;
+    const size_t paddingZeroLength = JpegEncoderHelper::kCompressBatchSize
+            * JpegEncoderHelper::kCompressBatchSize / 4;
+    std::unique_ptr<uint8_t[]> imageWithPaddingZeros(
+            new uint8_t[UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2
+            + paddingZeroLength]);
+    memcpy(imageWithPaddingZeros.get(), mUnalignedImage.buffer.get(),
+            UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2);
+    EXPECT_TRUE(encoder.compressImage(imageWithPaddingZeros.get(), mUnalignedImage.width,
+                                      mUnalignedImage.height, JPEG_QUALITY, NULL, 0));
+    ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+TEST_F(JpegEncoderHelperTest, encodeSingleChannelImage) {
+    JpegEncoderHelper encoder;
+    EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width,
+                                         mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true));
+    ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+}  // namespace android::ultrahdr
+
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
similarity index 86%
rename from libs/jpegrecoverymap/tests/jpegr_test.cpp
rename to libs/ultrahdr/tests/jpegr_test.cpp
index 620f431..58cd8f4 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/jpegr.h>
-#include <jpegrecoverymap/jpegrutils.h>
-#include <jpegrecoverymap/gainmapmath.h>
+#include <ultrahdr/jpegr.h>
+#include <ultrahdr/jpegrutils.h>
+#include <ultrahdr/gainmapmath.h>
 #include <fcntl.h>
 #include <fstream>
 #include <gtest/gtest.h>
@@ -36,7 +36,7 @@
 #define SAVE_DECODING_RESULT true
 #define SAVE_INPUT_RGBA true
 
-namespace android::jpegrecoverymap {
+namespace android::ultrahdr {
 
 struct Timer {
   struct timeval StartingTime;
@@ -118,16 +118,16 @@
 class JpegRBenchmark : public JpegR {
 public:
  void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
-                               jr_metadata_ptr metadata, jr_uncompressed_ptr map);
+                               ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
  void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
-                            jr_metadata_ptr metadata, jr_uncompressed_ptr dest);
+                            ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
 private:
  const int kProfileCount = 10;
 };
 
 void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
                                               jr_uncompressed_ptr p010Image,
-                                              jr_metadata_ptr metadata,
+                                              ultrahdr_metadata_ptr metadata,
                                               jr_uncompressed_ptr map) {
   ASSERT_EQ(yuv420Image->width, p010Image->width);
   ASSERT_EQ(yuv420Image->height, p010Image->height);
@@ -137,7 +137,7 @@
   timerStart(&genRecMapTime);
   for (auto i = 0; i < kProfileCount; i++) {
       ASSERT_EQ(OK, generateGainMap(
-          yuv420Image, p010Image, jpegr_transfer_function::JPEGR_TF_HLG, metadata, map));
+          yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, metadata, map));
       if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
   }
   timerStop(&genRecMapTime);
@@ -150,13 +150,13 @@
 
 void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image,
                                            jr_uncompressed_ptr map,
-                                           jr_metadata_ptr metadata,
+                                           ultrahdr_metadata_ptr metadata,
                                            jr_uncompressed_ptr dest) {
   Timer applyRecMapTime;
 
   timerStart(&applyRecMapTime);
   for (auto i = 0; i < kProfileCount; i++) {
-      ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG,
+      ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
                                  metadata->maxContentBoost /* displayBoost */, dest));
   }
   timerStop(&applyRecMapTime);
@@ -169,17 +169,18 @@
 TEST_F(JpegRTest, build) {
   // Force all of the gain map lib to be linked by calling all public functions.
   JpegR jpegRCodec;
-  jpegRCodec.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
-  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+  jpegRCodec.encodeJPEGR(nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr, 0, nullptr);
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
                          nullptr, 0, nullptr);
-  jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
                          nullptr);
-  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr);
   jpegRCodec.decodeJPEGR(nullptr, nullptr);
 }
 
 TEST_F(JpegRTest, writeXmpThenRead) {
-  jpegr_metadata_struct metadata_expected;
+  ultrahdr_metadata_struct metadata_expected;
+  metadata_expected.version = "1.0";
   metadata_expected.maxContentBoost = 1.25;
   metadata_expected.minContentBoost = 0.75;
   const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -194,7 +195,7 @@
   xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
                   reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
 
-  jpegr_metadata_struct metadata_read;
+  ultrahdr_metadata_struct metadata_read;
   EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
   EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
   EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
@@ -210,7 +211,7 @@
   }
   mRawP010Image.width = TEST_IMAGE_WIDTH;
   mRawP010Image.height = TEST_IMAGE_HEIGHT;
-  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   JpegR jpegRCodec;
 
@@ -218,7 +219,8 @@
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
   ret = jpegRCodec.encodeJPEGR(
-      &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
+      &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
+      nullptr);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -264,7 +266,7 @@
   mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
   mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
   mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
-  mRawP010ImageWithStride.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   JpegR jpegRCodec;
 
@@ -272,7 +274,7 @@
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
   ret = jpegRCodec.encodeJPEGR(
-      &mRawP010ImageWithStride, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
+      &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
       DEFAULT_JPEG_QUALITY, nullptr);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
@@ -318,14 +320,14 @@
   }
   mRawP010Image.width = TEST_IMAGE_WIDTH;
   mRawP010Image.height = TEST_IMAGE_HEIGHT;
-  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
     FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
   }
   mRawYuv420Image.width = TEST_IMAGE_WIDTH;
   mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
-  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+  mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
 
   JpegR jpegRCodec;
 
@@ -333,7 +335,7 @@
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
   ret = jpegRCodec.encodeJPEGR(
-      &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
+      &mRawP010Image, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
       DEFAULT_JPEG_QUALITY, nullptr);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
@@ -379,19 +381,19 @@
   }
   mRawP010Image.width = TEST_IMAGE_WIDTH;
   mRawP010Image.height = TEST_IMAGE_HEIGHT;
-  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
     FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
   }
   mRawYuv420Image.width = TEST_IMAGE_WIDTH;
   mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
-  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+  mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
 
   if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
     FAIL() << "Load file " << JPEG_IMAGE << " failed";
   }
-  mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+  mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
 
   JpegR jpegRCodec;
 
@@ -399,7 +401,8 @@
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
   ret = jpegRCodec.encodeJPEGR(
-      &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+      &mRawP010Image, &mRawYuv420Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+      &jpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -444,7 +447,7 @@
   }
   mRawP010Image.width = TEST_IMAGE_WIDTH;
   mRawP010Image.height = TEST_IMAGE_HEIGHT;
-  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   if (SAVE_INPUT_RGBA) {
     size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
@@ -472,7 +475,7 @@
   if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
     FAIL() << "Load file " << JPEG_IMAGE << " failed";
   }
-  mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+  mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
 
   JpegR jpegRCodec;
 
@@ -480,7 +483,7 @@
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
   ret = jpegRCodec.encodeJPEGR(
-      &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+      &mRawP010Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -525,25 +528,25 @@
   }
   mRawP010Image.width = kWidth;
   mRawP010Image.height = kHeight;
-  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+  mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
 
   if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
     FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
   }
   mRawYuv420Image.width = kWidth;
   mRawYuv420Image.height = kHeight;
-  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+  mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
 
   JpegRBenchmark benchmark;
 
-  jpegr_metadata_struct metadata = { .version = 1,
+  ultrahdr_metadata_struct metadata = { .version = "1.0",
                               .maxContentBoost = 8.0f,
                               .minContentBoost = 1.0f / 8.0f };
 
   jpegr_uncompressed_struct map = { .data = NULL,
                                     .width = 0,
                                     .height = 0,
-                                    .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };
+                                    .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
 
   benchmark.BenchmarkGenerateGainMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);
 
@@ -552,9 +555,9 @@
   jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
                                      .width = 0,
                                      .height = 0,
-                                     .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };
+                                     .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
 
   benchmark.BenchmarkApplyGainMap(&mRawYuv420Image, &map, &metadata, &dest);
 }
 
-} // namespace android::recoverymap
+} // namespace android::ultrahdr
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 501bf58..32c21f6 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -697,6 +697,11 @@
 #define EGL_EXT_device_query 1
 #endif /* EGL_EXT_device_query */
 
+#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
+#define EGL_EXT_gl_colorspace_bt2020_hlg 1
+#define EGL_GL_COLORSPACE_BT2020_HLG_EXT  0x333E
+#endif /* EGL_EXT_gl_colorspace_bt2020_hlg */
+
 #ifndef EGL_EXT_gl_colorspace_bt2020_linear
 #define EGL_EXT_gl_colorspace_bt2020_linear 1
 #define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index aecfc6b..b3a4bc1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -15,6 +15,7 @@
  */
 
 //#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "BlobCache.h"
 
@@ -22,6 +23,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <log/log.h>
+#include <utils/Trace.h>
 
 #include <chrono>
 
@@ -230,6 +232,8 @@
 }
 
 int BlobCache::unflatten(void const* buffer, size_t size) {
+    ATRACE_NAME("BlobCache::unflatten");
+
     // All errors should result in the BlobCache being in an empty state.
     clear();
 
@@ -293,6 +297,8 @@
 }
 
 void BlobCache::clean() {
+    ATRACE_NAME("BlobCache::clean");
+
     // Remove a random cache entry until the total cache size gets below half
     // the maximum total cache size.
     while (mTotalSize > mMaxTotalSize / 2) {
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 1026842..4a0fac4 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -14,6 +14,8 @@
  ** limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "FileBlobCache.h"
 
 #include <errno.h>
@@ -24,6 +26,7 @@
 #include <unistd.h>
 
 #include <log/log.h>
+#include <utils/Trace.h>
 
 // Cache file header
 static const char* cacheFileMagic = "EGL$";
@@ -51,6 +54,8 @@
         const std::string& filename)
         : BlobCache(maxKeySize, maxValueSize, maxTotalSize)
         , mFilename(filename) {
+    ATRACE_CALL();
+
     if (mFilename.length() > 0) {
         size_t headerSize = cacheFileHeaderSize;
 
@@ -117,6 +122,8 @@
 }
 
 void FileBlobCache::writeToFile() {
+    ATRACE_CALL();
+
     if (mFilename.length() > 0) {
         size_t cacheSize = getFlattenedSize();
         size_t headerSize = cacheFileHeaderSize;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index c2c856e..9823fc8 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -353,8 +353,9 @@
             // Typically that means there is an HDR capable display attached, but could be
             // support for attaching an HDR display. In either case, advertise support for
             // HDR color spaces.
-            mExtensionString.append(
-                    "EGL_EXT_gl_colorspace_bt2020_linear EGL_EXT_gl_colorspace_bt2020_pq ");
+            mExtensionString.append("EGL_EXT_gl_colorspace_bt2020_hlg "
+                                    "EGL_EXT_gl_colorspace_bt2020_linear "
+                                    "EGL_EXT_gl_colorspace_bt2020_pq ");
         }
 
         char const* start = gExtensionString;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 2bca14d..48718bb 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,6 +18,7 @@
 
 #include "egl_platform_entries.h"
 
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/hardware_buffer.h>
@@ -29,7 +30,6 @@
 #include <private/android/AHardwareBufferHelpers.h>
 #include <stdlib.h>
 #include <string.h>
-#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 
 #include <condition_variable>
 #include <deque>
@@ -421,11 +421,14 @@
         return HAL_DATASPACE_V0_SCRGB;
     } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
         return HAL_DATASPACE_V0_SCRGB_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_HLG_EXT) {
+        return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
     } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
         return HAL_DATASPACE_BT2020_LINEAR;
     } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
         return HAL_DATASPACE_BT2020_PQ;
     }
+
     return HAL_DATASPACE_UNKNOWN;
 }
 
@@ -452,6 +455,9 @@
     if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) {
         colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
     }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_hlg")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_HLG_EXT);
+    }
     if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) {
         colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
     }
@@ -485,6 +491,7 @@
                 case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
                 case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
                 case EGL_GL_COLORSPACE_SCRGB_EXT:
+                case EGL_GL_COLORSPACE_BT2020_HLG_EXT:
                 case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
                 case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
                 case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 614a78e..72bd292 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -53,6 +53,4 @@
     vintf_fragments: [
         "manifest_android.frameworks.automotive.display@1.0.xml",
     ],
-
-    system_ext_specific: true,
 }
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index ea1077a..5c7f344 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -1,4 +1,4 @@
-service automotive_display /system_ext/bin/android.frameworks.automotive.display@1.0-service
+service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service
     class hal
     user graphics
     group automotive_evs
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index aaa8c18..5e7b2e8 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -19,6 +19,7 @@
 #include "GpuService.h"
 
 #include <android-base/stringprintf.h>
+#include <android-base/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
 #include <binder/Parcel.h>
@@ -46,6 +47,8 @@
 } // namespace
 
 const String16 sDump("android.permission.DUMP");
+const String16 sAccessGpuServicePermission("android.permission.ACCESS_GPU_SERVICE");
+const std::string sAngleGlesDriverSuffix = "angle";
 
 const char* const GpuService::SERVICE_NAME = "gpu";
 
@@ -88,6 +91,29 @@
     mGpuStats->insertTargetStatsArray(appPackageName, driverVersionCode, stats, values, valueCount);
 }
 
+void GpuService::toggleAngleAsSystemDriver(bool enabled) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+
+    // only system_server with the ACCESS_GPU_SERVICE permission is allowed to set
+    // persist.graphics.egl
+    if (uid != AID_SYSTEM ||
+        !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
+        ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
+                "pid=%d, uid=%d\n", pid, uid);
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mLock);
+    if (enabled) {
+        android::base::SetProperty("persist.graphics.egl", sAngleGlesDriverSuffix);
+    } else {
+        android::base::SetProperty("persist.graphics.egl", "");
+    }
+}
+
+
 void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index e7e0cba..0e559f2 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -61,6 +61,7 @@
                         const uint64_t* values, const uint32_t valueCount) override;
     void setUpdatableDriverPath(const std::string& driverPath) override;
     std::string getUpdatableDriverPath() override;
+    void toggleAngleAsSystemDriver(bool enabled) override;
 
     /*
      * IBinder interface
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 1bc1adf..aa55873 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -47,16 +47,14 @@
 void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
     Visitor v{
             [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
-            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(&args); },
-            [&](const NotifyKeyArgs& args) { notifyKey(&args); },
-            [&](const NotifyMotionArgs& args) { notifyMotion(&args); },
-            [&](const NotifySwitchArgs& args) { notifySwitch(&args); },
-            [&](const NotifySensorArgs& args) { notifySensor(&args); },
-            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(&args); },
-            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(&args); },
-            [&](const NotifyPointerCaptureChangedArgs& args) {
-                notifyPointerCaptureChanged(&args);
-            },
+            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },
+            [&](const NotifyKeyArgs& args) { notifyKey(args); },
+            [&](const NotifyMotionArgs& args) { notifyMotion(args); },
+            [&](const NotifySwitchArgs& args) { notifySwitch(args); },
+            [&](const NotifySensorArgs& args) { notifySensor(args); },
+            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },
+            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },
+            [&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },
     };
     std::visit(v, generalArgs);
 }
@@ -78,45 +76,44 @@
     mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyConfigurationChanged(
-        const NotifyConfigurationChangedArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyMotion(const NotifyMotionArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifySwitch(const NotifySwitchArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifySensor(const NotifySensorArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
-void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-    traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(*args);
+void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
+    traceEvent(__func__, args.id);
+    mArgsQueue.emplace_back(args);
 }
 
 void QueuedInputListener::flush() {
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 6c0bcff..7a84be9 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -419,63 +419,63 @@
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
     // pass through
     mQueuedListener.notifyConfigurationChanged(args);
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyKey(const NotifyKeyArgs* args) {
+void InputProcessor::notifyKey(const NotifyKeyArgs& args) {
     // pass through
     mQueuedListener.notifyKey(args);
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyMotion(const NotifyMotionArgs* args) {
+void InputProcessor::notifyMotion(const NotifyMotionArgs& args) {
     { // acquire lock
         std::scoped_lock lock(mLock);
         // MotionClassifier is only used for touch events, for now
-        const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
+        const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(args);
         if (!sendToMotionClassifier) {
             mQueuedListener.notifyMotion(args);
         } else {
-            NotifyMotionArgs newArgs(*args);
+            NotifyMotionArgs newArgs(args);
             const MotionClassification newClassification = mMotionClassifier->classify(newArgs);
-            LOG_ALWAYS_FATAL_IF(args->classification != MotionClassification::NONE &&
+            LOG_ALWAYS_FATAL_IF(args.classification != MotionClassification::NONE &&
                                         newClassification != MotionClassification::NONE,
                                 "Conflicting classifications %s (new) and %s (old)!",
                                 motionClassificationToString(newClassification),
-                                motionClassificationToString(args->classification));
+                                motionClassificationToString(args.classification));
             newArgs.classification = newClassification;
-            mQueuedListener.notifyMotion(&newArgs);
+            mQueuedListener.notifyMotion(newArgs);
         }
     } // release lock
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifySensor(const NotifySensorArgs* args) {
+void InputProcessor::notifySensor(const NotifySensorArgs& args) {
     // pass through
     mQueuedListener.notifySensor(args);
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+void InputProcessor::notifyVibratorState(const NotifyVibratorStateArgs& args) {
     // pass through
     mQueuedListener.notifyVibratorState(args);
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifySwitch(const NotifySwitchArgs* args) {
+void InputProcessor::notifySwitch(const NotifySwitchArgs& args) {
     // pass through
     mQueuedListener.notifySwitch(args);
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+void InputProcessor::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
     { // acquire lock
         std::scoped_lock lock(mLock);
         if (mMotionClassifier) {
-            mMotionClassifier->reset(*args);
+            mMotionClassifier->reset(args);
         }
     } // release lock
 
@@ -484,7 +484,7 @@
     mQueuedListener.flush();
 }
 
-void InputProcessor::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+void InputProcessor::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
     // pass through
     mQueuedListener.notifyPointerCaptureChanged(args);
     mQueuedListener.flush();
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index 01795a8..dcbfebc 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -246,14 +246,14 @@
     explicit InputProcessor(InputListenerInterface& listener);
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
-    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    void notifyKey(const NotifyKeyArgs* args) override;
-    void notifyMotion(const NotifyMotionArgs* args) override;
-    void notifySwitch(const NotifySwitchArgs* args) override;
-    void notifySensor(const NotifySensorArgs* args) override;
-    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+    void notifyKey(const NotifyKeyArgs& args) override;
+    void notifyMotion(const NotifyMotionArgs& args) override;
+    void notifySwitch(const NotifySwitchArgs& args) override;
+    void notifySensor(const NotifySensorArgs& args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
     void dump(std::string& dump) override;
     void monitor() override;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 2450235..4ec5b89 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -38,50 +38,6 @@
 
 // --- InputReaderConfiguration ---
 
-std::string InputReaderConfiguration::changesToString(uint32_t changes) {
-    if (changes == 0) {
-        return "<none>";
-    }
-    std::string result;
-    if (changes & CHANGE_POINTER_SPEED) {
-        result += "POINTER_SPEED | ";
-    }
-    if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) {
-        result += "POINTER_GESTURE_ENABLEMENT | ";
-    }
-    if (changes & CHANGE_DISPLAY_INFO) {
-        result += "DISPLAY_INFO | ";
-    }
-    if (changes & CHANGE_SHOW_TOUCHES) {
-        result += "SHOW_TOUCHES | ";
-    }
-    if (changes & CHANGE_KEYBOARD_LAYOUTS) {
-        result += "KEYBOARD_LAYOUTS | ";
-    }
-    if (changes & CHANGE_DEVICE_ALIAS) {
-        result += "DEVICE_ALIAS | ";
-    }
-    if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) {
-        result += "TOUCH_AFFINE_TRANSFORMATION | ";
-    }
-    if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
-        result += "EXTERNAL_STYLUS_PRESENCE | ";
-    }
-    if (changes & CHANGE_POINTER_CAPTURE) {
-        result += "POINTER_CAPTURE | ";
-    }
-    if (changes & CHANGE_ENABLED_STATE) {
-        result += "ENABLED_STATE | ";
-    }
-    if (changes & CHANGE_TOUCHPAD_SETTINGS) {
-        result += "TOUCHPAD_SETTINGS | ";
-    }
-    if (changes & CHANGE_MUST_REOPEN) {
-        result += "MUST_REOPEN | ";
-    }
-    return result;
-}
-
 std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
         const std::string& uniqueDisplayId) const {
     if (uniqueDisplayId.empty()) {
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 6d43e8d..02bc47d 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -329,24 +329,24 @@
       : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {}
 
 void UnwantedInteractionBlocker::notifyConfigurationChanged(
-        const NotifyConfigurationChangedArgs* args) {
+        const NotifyConfigurationChangedArgs& args) {
     mQueuedListener.notifyConfigurationChanged(args);
     mQueuedListener.flush();
 }
 
-void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) {
+void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs& args) {
     mQueuedListener.notifyKey(args);
     mQueuedListener.flush();
 }
 
-void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
-    ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args->dump().c_str());
+void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs& args) {
+    ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
     { // acquire lock
         std::scoped_lock lock(mLock);
         const std::vector<NotifyMotionArgs> processedArgs =
-                mPreferStylusOverTouchBlocker.processMotion(*args);
+                mPreferStylusOverTouchBlocker.processMotion(args);
         for (const NotifyMotionArgs& loopArgs : processedArgs) {
-            notifyMotionLocked(&loopArgs);
+            notifyMotionLocked(loopArgs);
         }
     } // release lock
 
@@ -356,56 +356,56 @@
 
 void UnwantedInteractionBlocker::enqueueOutboundMotionLocked(const NotifyMotionArgs& args) {
     ALOGD_IF(DEBUG_OUTBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
-    mQueuedListener.notifyMotion(&args);
+    mQueuedListener.notifyMotion(args);
 }
 
-void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) {
-    auto it = mPalmRejectors.find(args->deviceId);
-    const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
+void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs& args) {
+    auto it = mPalmRejectors.find(args.deviceId);
+    const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args.source);
     if (!sendToPalmRejector) {
-        enqueueOutboundMotionLocked(*args);
+        enqueueOutboundMotionLocked(args);
         return;
     }
 
-    std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(*args);
+    std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(args);
     for (const NotifyMotionArgs& loopArgs : processedArgs) {
         enqueueOutboundMotionLocked(loopArgs);
     }
 }
 
-void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) {
+void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs& args) {
     mQueuedListener.notifySwitch(args);
     mQueuedListener.flush();
 }
 
-void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) {
+void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs& args) {
     mQueuedListener.notifySensor(args);
     mQueuedListener.flush();
 }
 
-void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs& args) {
     mQueuedListener.notifyVibratorState(args);
     mQueuedListener.flush();
 }
-void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
     { // acquire lock
         std::scoped_lock lock(mLock);
-        auto it = mPalmRejectors.find(args->deviceId);
+        auto it = mPalmRejectors.find(args.deviceId);
         if (it != mPalmRejectors.end()) {
             AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo();
             // Re-create the object instead of resetting it
             mPalmRejectors.erase(it);
-            mPalmRejectors.emplace(args->deviceId, info);
+            mPalmRejectors.emplace(args.deviceId, info);
         }
         mQueuedListener.notifyDeviceReset(args);
-        mPreferStylusOverTouchBlocker.notifyDeviceReset(*args);
+        mPreferStylusOverTouchBlocker.notifyDeviceReset(args);
     } // release lock
     // Send events to the next stage without holding the lock
     mQueuedListener.flush();
 }
 
 void UnwantedInteractionBlocker::notifyPointerCaptureChanged(
-        const NotifyPointerCaptureChangedArgs* args) {
+        const NotifyPointerCaptureChangedArgs& args) {
     mQueuedListener.notifyPointerCaptureChanged(args);
     mQueuedListener.flush();
 }
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 3bc5240..419da83 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -91,14 +91,14 @@
     explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection);
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
-    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    void notifyKey(const NotifyKeyArgs* args) override;
-    void notifyMotion(const NotifyMotionArgs* args) override;
-    void notifySwitch(const NotifySwitchArgs* args) override;
-    void notifySensor(const NotifySensorArgs* args) override;
-    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+    void notifyKey(const NotifyKeyArgs& args) override;
+    void notifyMotion(const NotifyMotionArgs& args) override;
+    void notifySwitch(const NotifySwitchArgs& args) override;
+    void notifySensor(const NotifySensorArgs& args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
     void dump(std::string& dump) override;
     void monitor() override;
@@ -119,7 +119,7 @@
     // Use a separate palm rejector for every touch device.
     std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors GUARDED_BY(mLock);
     // TODO(b/210159205): delete this when simultaneous stylus and touch is supported
-    void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+    void notifyMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
 
     // Call this function for outbound events so that they can be logged when logging is enabled.
     void enqueueOutboundMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 58324c4..f852001 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -277,12 +277,12 @@
         motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
         motionArgs.downTime = now();
         motionArgs.eventTime = motionArgs.downTime;
-        dispatcher.notifyMotion(&motionArgs);
+        dispatcher.notifyMotion(motionArgs);
 
         // Send ACTION_UP
         motionArgs.action = AMOTION_EVENT_ACTION_UP;
         motionArgs.eventTime = now();
-        dispatcher.notifyMotion(&motionArgs);
+        dispatcher.notifyMotion(motionArgs);
 
         window->consumeEvent();
         window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index b4497fd..ed95de7 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -28,8 +28,6 @@
         inputPublisher(inputChannel),
         inputState(idGenerator) {}
 
-Connection::~Connection() {}
-
 const std::string Connection::getWindowName() const {
     if (inputChannel != nullptr) {
         return inputChannel->getName();
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index 6040e9b..2929d61 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -27,10 +27,7 @@
 struct DispatchEntry;
 
 /* Manages the dispatch state associated with a single input channel. */
-class Connection : public RefBase {
-protected:
-    virtual ~Connection();
-
+class Connection {
 public:
     enum class Status {
         // Everything is peachy.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c39c408..6b9ad44 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -688,7 +688,7 @@
     mCommandQueue.clear();
 
     while (!mConnectionsByToken.empty()) {
-        sp<Connection> connection = mConnectionsByToken.begin()->second;
+        std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
         removeInputChannelLocked(connection->inputChannel->getConnectionToken(), /*notify=*/false);
     }
 }
@@ -802,7 +802,7 @@
     }
 
     // If we reached here, we have an unresponsive connection.
-    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+    std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
     if (connection == nullptr) {
         ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
         return nextAnrCheck;
@@ -815,7 +815,7 @@
 }
 
 std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
-        const sp<Connection>& connection) {
+        const std::shared_ptr<Connection>& connection) {
     if (connection->monitor) {
         return mMonitorDispatchingTimeout;
     }
@@ -1058,7 +1058,8 @@
         const std::vector<sp<WindowInfoHandle>> touchedSpies =
                 findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
         for (const auto& windowHandle : touchedSpies) {
-            const sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
+            const std::shared_ptr<Connection> connection =
+                    getConnectionLocked(windowHandle->getToken());
             if (connection != nullptr && connection->responsive) {
                 // This spy window could take more input. Drop all events preceding this
                 // event, so that the spy window can get a chance to receive the stream.
@@ -1297,7 +1298,7 @@
             (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
 }
 
-bool InputDispatcher::isAppSwitchPendingLocked() {
+bool InputDispatcher::isAppSwitchPendingLocked() const {
     return mAppSwitchDueTime != LLONG_MAX;
 }
 
@@ -1895,7 +1896,7 @@
     pokeUserActivityLocked(*eventEntry);
 
     for (const InputTarget& inputTarget : inputTargets) {
-        sp<Connection> connection =
+        std::shared_ptr<Connection> connection =
                 getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
         if (connection != nullptr) {
             prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
@@ -1909,7 +1910,7 @@
     }
 }
 
-void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection>& connection) {
     // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
     // If the policy decides to close the app, we will get a channel removal event via
     // unregisterInputChannel, and will clean up the connection that way. We are already not
@@ -2101,7 +2102,7 @@
     std::vector<Monitor> responsiveMonitors;
     std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
                  [this](const Monitor& monitor) REQUIRES(mLock) {
-                     sp<Connection> connection =
+                     std::shared_ptr<Connection> connection =
                              getConnectionLocked(monitor.inputChannel->getConnectionToken());
                      if (connection == nullptr) {
                          ALOGE("Could not find connection for monitor %s",
@@ -3027,7 +3028,7 @@
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
-                                                 const sp<Connection>& connection,
+                                                 const std::shared_ptr<Connection>& connection,
                                                  std::shared_ptr<EventEntry> eventEntry,
                                                  const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
@@ -3097,7 +3098,7 @@
 }
 
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
-                                                   const sp<Connection>& connection,
+                                                   const std::shared_ptr<Connection>& connection,
                                                    std::shared_ptr<EventEntry> eventEntry,
                                                    const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
@@ -3131,7 +3132,7 @@
     }
 }
 
-void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
+void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
                                                  std::shared_ptr<EventEntry> eventEntry,
                                                  const InputTarget& inputTarget,
                                                  ftl::Flags<InputTarget::Flags> dispatchMode) {
@@ -3316,14 +3317,14 @@
     }
 
     std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
-    std::vector<sp<Connection>> newConnections;
+    std::vector<std::shared_ptr<Connection>> newConnections;
     for (const InputTarget& target : targets) {
         if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
             continue; // Skip windows that receive ACTION_OUTSIDE
         }
 
         sp<IBinder> token = target.inputChannel->getConnectionToken();
-        sp<Connection> connection = getConnectionLocked(token);
+        std::shared_ptr<Connection> connection = getConnectionLocked(token);
         if (connection == nullptr) {
             continue;
         }
@@ -3336,7 +3337,7 @@
     mInteractionConnectionTokens = newConnectionTokens;
 
     std::string targetList;
-    for (const sp<Connection>& connection : newConnections) {
+    for (const std::shared_ptr<Connection>& connection : newConnections) {
         targetList += connection->getWindowName() + ", ";
     }
     std::string message = "Interaction with: " + targetList;
@@ -3416,7 +3417,7 @@
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
-                                               const sp<Connection>& connection) {
+                                               const std::shared_ptr<Connection>& connection) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
                                            connection->getInputChannelName().c_str());
@@ -3596,8 +3597,8 @@
 }
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
-                                                const sp<Connection>& connection, uint32_t seq,
-                                                bool handled, nsecs_t consumeTime) {
+                                                const std::shared_ptr<Connection>& connection,
+                                                uint32_t seq, bool handled, nsecs_t consumeTime) {
     if (DEBUG_DISPATCH_CYCLE) {
         ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
               connection->getInputChannelName().c_str(), seq, toString(handled));
@@ -3616,7 +3617,7 @@
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
-                                                     const sp<Connection>& connection,
+                                                     const std::shared_ptr<Connection>& connection,
                                                      bool notify) {
     if (DEBUG_DISPATCH_CYCLE) {
         LOG(DEBUG) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
@@ -3665,7 +3666,7 @@
 
 int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
     std::scoped_lock _l(mLock);
-    sp<Connection> connection = getConnectionLocked(connectionToken);
+    std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
     if (connection == nullptr) {
         ALOGW("Received looper callback for unknown input channel token %p.  events=0x%x",
               connectionToken.get(), events);
@@ -3757,7 +3758,7 @@
 
 void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
         const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
-    sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
+    std::shared_ptr<Connection> connection = getConnectionLocked(channel->getConnectionToken());
     if (connection == nullptr) {
         return;
     }
@@ -3766,7 +3767,7 @@
 }
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
-        const sp<Connection>& connection, const CancelationOptions& options) {
+        const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
     if (connection->status == Connection::Status::BROKEN) {
         return;
     }
@@ -3844,7 +3845,7 @@
 }
 
 void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
-        const nsecs_t downTime, const sp<Connection>& connection,
+        const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
         ftl::Flags<InputTarget::Flags> targetFlags) {
     if (connection->status == Connection::Status::BROKEN) {
         return;
@@ -3909,7 +3910,8 @@
 void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
         const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) {
     if (windowHandle != nullptr) {
-        sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken());
+        std::shared_ptr<Connection> wallpaperConnection =
+                getConnectionLocked(windowHandle->getToken());
         if (wallpaperConnection != nullptr) {
             synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options);
         }
@@ -4025,9 +4027,9 @@
     return splitMotionEntry;
 }
 
-void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
     if (debugInboundEventDetails()) {
-        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
+        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
     }
 
     bool needWake = false;
@@ -4035,7 +4037,7 @@
         std::scoped_lock _l(mLock);
 
         std::unique_ptr<ConfigurationChangedEntry> newEntry =
-                std::make_unique<ConfigurationChangedEntry>(args->id, args->eventTime);
+                std::make_unique<ConfigurationChangedEntry>(args.id, args.eventTime);
         needWake = enqueueInboundEventLocked(std::move(newEntry));
     } // release lock
 
@@ -4083,23 +4085,22 @@
     }
 }
 
-void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
+void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
     ALOGD_IF(debugInboundEventDetails(),
              "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
              ", deviceId=%d, source=%s, displayId=%" PRId32
              "policyFlags=0x%x, action=%s, flags=0x%x, keyCode=%s, scanCode=0x%x, metaState=0x%x, "
              "downTime=%" PRId64,
-             args->id, args->eventTime, args->deviceId,
-             inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
-             KeyEvent::actionToString(args->action), args->flags, KeyEvent::getLabel(args->keyCode),
-             args->scanCode, args->metaState, args->downTime);
-    if (!validateKeyEvent(args->action)) {
+             args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
+             args.displayId, args.policyFlags, KeyEvent::actionToString(args.action), args.flags,
+             KeyEvent::getLabel(args.keyCode), args.scanCode, args.metaState, args.downTime);
+    if (!validateKeyEvent(args.action)) {
         return;
     }
 
-    uint32_t policyFlags = args->policyFlags;
-    int32_t flags = args->flags;
-    int32_t metaState = args->metaState;
+    uint32_t policyFlags = args.policyFlags;
+    int32_t flags = args.flags;
+    int32_t metaState = args.metaState;
     // InputDispatcher tracks and generates key repeats on behalf of
     // whatever notifies it, so repeatCount should always be set to 0
     constexpr int32_t repeatCount = 0;
@@ -4113,13 +4114,13 @@
 
     policyFlags |= POLICY_FLAG_TRUSTED;
 
-    int32_t keyCode = args->keyCode;
-    accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
+    int32_t keyCode = args.keyCode;
+    accelerateMetaShortcuts(args.deviceId, args.action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
-                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
-                     args->downTime, args->eventTime);
+    event.initialize(args.id, args.deviceId, args.source, args.displayId, INVALID_HMAC, args.action,
+                     flags, keyCode, args.scanCode, metaState, repeatCount, args.downTime,
+                     args.eventTime);
 
     android::base::Timer t;
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -4144,10 +4145,9 @@
         }
 
         std::unique_ptr<KeyEntry> newEntry =
-                std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
-                                           args->displayId, policyFlags, args->action, flags,
-                                           keyCode, args->scanCode, metaState, repeatCount,
-                                           args->downTime);
+                std::make_unique<KeyEntry>(args.id, args.eventTime, args.deviceId, args.source,
+                                           args.displayId, policyFlags, args.action, flags, keyCode,
+                                           args.scanCode, metaState, repeatCount, args.downTime);
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
@@ -4158,50 +4158,50 @@
     }
 }
 
-bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) {
+bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs& args) {
     return mInputFilterEnabled;
 }
 
-void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
+void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
     if (debugInboundEventDetails()) {
         ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
               "displayId=%" PRId32 ", policyFlags=0x%x, "
               "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
               "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
               "yCursorPosition=%f, downTime=%" PRId64,
-              args->id, args->eventTime, args->deviceId,
-              inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
-              MotionEvent::actionToString(args->action).c_str(), args->actionButton, args->flags,
-              args->metaState, args->buttonState, args->edgeFlags, args->xPrecision,
-              args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime);
-        for (uint32_t i = 0; i < args->pointerCount; i++) {
+              args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
+              args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),
+              args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
+              args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
+              args.downTime);
+        for (uint32_t i = 0; i < args.pointerCount; i++) {
             ALOGD("  Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
                   "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
-                  i, args->pointerProperties[i].id,
-                  ftl::enum_string(args->pointerProperties[i].toolType).c_str(),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+                  i, args.pointerProperties[i].id,
+                  ftl::enum_string(args.pointerProperties[i].toolType).c_str(),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
         }
     }
 
-    if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
-                             args->pointerProperties)) {
-        LOG(ERROR) << "Invalid event: " << args->dump();
+    if (!validateMotionEvent(args.action, args.actionButton, args.pointerCount,
+                             args.pointerProperties)) {
+        LOG(ERROR) << "Invalid event: " << args.dump();
         return;
     }
 
-    uint32_t policyFlags = args->policyFlags;
+    uint32_t policyFlags = args.policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
 
     android::base::Timer t;
-    mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
+    mPolicy->interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
               std::to_string(t.duration().count()).c_str());
@@ -4213,10 +4213,10 @@
         if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
             // Set the flag anyway if we already have an ongoing gesture. That would allow us to
             // complete the processing of the current stroke.
-            const auto touchStateIt = mTouchStatesByDisplay.find(args->displayId);
+            const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
             if (touchStateIt != mTouchStatesByDisplay.end()) {
                 const TouchState& touchState = touchStateIt->second;
-                if (touchState.deviceId == args->deviceId && touchState.isDown()) {
+                if (touchState.deviceId == args.deviceId && touchState.isDown()) {
                     policyFlags |= POLICY_FLAG_PASS_TO_USER;
                 }
             }
@@ -4224,20 +4224,20 @@
 
         if (shouldSendMotionToInputFilterLocked(args)) {
             ui::Transform displayTransform;
-            if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+            if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {
                 displayTransform = it->second.transform;
             }
 
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
-                             args->action, args->actionButton, args->flags, args->edgeFlags,
-                             args->metaState, args->buttonState, args->classification,
-                             displayTransform, args->xPrecision, args->yPrecision,
-                             args->xCursorPosition, args->yCursorPosition, displayTransform,
-                             args->downTime, args->eventTime, args->pointerCount,
-                             args->pointerProperties, args->pointerCoords);
+            event.initialize(args.id, args.deviceId, args.source, args.displayId, INVALID_HMAC,
+                             args.action, args.actionButton, args.flags, args.edgeFlags,
+                             args.metaState, args.buttonState, args.classification,
+                             displayTransform, args.xPrecision, args.yPrecision,
+                             args.xCursorPosition, args.yCursorPosition, displayTransform,
+                             args.downTime, args.eventTime, args.pointerCount,
+                             args.pointerProperties, args.pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -4249,21 +4249,20 @@
 
         // Just enqueue a new motion event.
         std::unique_ptr<MotionEntry> newEntry =
-                std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
-                                              args->source, args->displayId, policyFlags,
-                                              args->action, args->actionButton, args->flags,
-                                              args->metaState, args->buttonState,
-                                              args->classification, args->edgeFlags,
-                                              args->xPrecision, args->yPrecision,
-                                              args->xCursorPosition, args->yCursorPosition,
-                                              args->downTime, args->pointerCount,
-                                              args->pointerProperties, args->pointerCoords);
+                std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
+                                              args.displayId, policyFlags, args.action,
+                                              args.actionButton, args.flags, args.metaState,
+                                              args.buttonState, args.classification, args.edgeFlags,
+                                              args.xPrecision, args.yPrecision,
+                                              args.xCursorPosition, args.yCursorPosition,
+                                              args.downTime, args.pointerCount,
+                                              args.pointerProperties, args.pointerCoords);
 
-        if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
-            IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
+        if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+            IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
             !mInputFilterEnabled) {
-            const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
-            mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
+            const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
+            mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);
         }
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
@@ -4275,12 +4274,12 @@
     }
 }
 
-void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
+void InputDispatcher::notifySensor(const NotifySensorArgs& args) {
     if (debugInboundEventDetails()) {
         ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
               " sensorType=%s",
-              args->id, args->eventTime, args->deviceId, args->source,
-              ftl::enum_string(args->sensorType).c_str());
+              args.id, args.eventTime, args.deviceId, args.source,
+              ftl::enum_string(args.sensorType).c_str());
     }
 
     bool needWake = false;
@@ -4289,10 +4288,9 @@
 
         // Just enqueue a new sensor event.
         std::unique_ptr<SensorEntry> newEntry =
-                std::make_unique<SensorEntry>(args->id, args->eventTime, args->deviceId,
-                                              args->source, /* policyFlags=*/0, args->hwTimestamp,
-                                              args->sensorType, args->accuracy,
-                                              args->accuracyChanged, args->values);
+                std::make_unique<SensorEntry>(args.id, args.eventTime, args.deviceId, args.source,
+                                              /* policyFlags=*/0, args.hwTimestamp, args.sensorType,
+                                              args.accuracy, args.accuracyChanged, args.values);
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
@@ -4303,34 +4301,34 @@
     }
 }
 
-void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs& args) {
     if (debugInboundEventDetails()) {
-        ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
-              args->deviceId, args->isOn);
+        ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args.eventTime,
+              args.deviceId, args.isOn);
     }
-    mPolicy->notifyVibratorState(args->deviceId, args->isOn);
+    mPolicy->notifyVibratorState(args.deviceId, args.isOn);
 }
 
-bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
+bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs& args) {
     return mInputFilterEnabled;
 }
 
-void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
+void InputDispatcher::notifySwitch(const NotifySwitchArgs& args) {
     if (debugInboundEventDetails()) {
         ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
               "switchMask=0x%08x",
-              args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+              args.eventTime, args.policyFlags, args.switchValues, args.switchMask);
     }
 
-    uint32_t policyFlags = args->policyFlags;
+    uint32_t policyFlags = args.policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags);
+    mPolicy->notifySwitch(args.eventTime, args.switchValues, args.switchMask, policyFlags);
 }
 
-void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
     if (debugInboundEventDetails()) {
-        ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
-              args->deviceId);
+        ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
+              args.deviceId);
     }
 
     bool needWake = false;
@@ -4338,7 +4336,7 @@
         std::scoped_lock _l(mLock);
 
         std::unique_ptr<DeviceResetEntry> newEntry =
-                std::make_unique<DeviceResetEntry>(args->id, args->eventTime, args->deviceId);
+                std::make_unique<DeviceResetEntry>(args.id, args.eventTime, args.deviceId);
         needWake = enqueueInboundEventLocked(std::move(newEntry));
     } // release lock
 
@@ -4347,17 +4345,17 @@
     }
 }
 
-void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
     if (debugInboundEventDetails()) {
-        ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-              args->request.enable ? "true" : "false");
+        ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime,
+              args.request.enable ? "true" : "false");
     }
 
     bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
-        auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
-                                                                  args->request);
+        auto entry =
+                std::make_unique<PointerCaptureChangedEntry>(args.id, args.eventTime, args.request);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
 
@@ -4800,7 +4798,7 @@
         return false;
     }
 
-    sp<Connection> connection = getConnectionLocked(window->getToken());
+    std::shared_ptr<Connection> connection = getConnectionLocked(window->getToken());
     if (connection == nullptr) {
         ALOGW("Not sending touch to %s because there's no corresponding connection",
               window->getName().c_str());
@@ -5322,8 +5320,8 @@
         }
 
         // Synthesize cancel for old window and down for new window.
-        sp<Connection> fromConnection = getConnectionLocked(fromToken);
-        sp<Connection> toConnection = getConnectionLocked(toToken);
+        std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken);
+        std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
         if (fromConnection != nullptr && toConnection != nullptr) {
             fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
             CancelationOptions
@@ -5415,7 +5413,7 @@
     mReplacedKeys.clear();
 }
 
-void InputDispatcher::logDispatchStateLocked() {
+void InputDispatcher::logDispatchStateLocked() const {
     std::string dump;
     dumpDispatchStateLocked(dump);
 
@@ -5427,7 +5425,7 @@
     }
 }
 
-std::string InputDispatcher::dumpPointerCaptureStateLocked() {
+std::string InputDispatcher::dumpPointerCaptureStateLocked() const {
     std::string dump;
 
     dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
@@ -5445,7 +5443,7 @@
     return dump;
 }
 
-void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
+void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const {
     dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
     dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
     dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled));
@@ -5548,7 +5546,7 @@
     // Dump recently dispatched or dropped events from oldest to newest.
     if (!mRecentQueue.empty()) {
         dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
-        for (std::shared_ptr<EventEntry>& entry : mRecentQueue) {
+        for (const std::shared_ptr<EventEntry>& entry : mRecentQueue) {
             dump += INDENT2;
             dump += entry->getDescription();
             dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5571,7 +5569,7 @@
     // Dump inbound events from oldest to newest.
     if (!mInboundQueue.empty()) {
         dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
-        for (std::shared_ptr<EventEntry>& entry : mInboundQueue) {
+        for (const std::shared_ptr<EventEntry>& entry : mInboundQueue) {
             dump += INDENT2;
             dump += entry->getDescription();
             dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5653,7 +5651,7 @@
     dump += mLatencyAggregator.dump(INDENT2);
 }
 
-void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
+void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const {
     const size_t numMonitors = monitors.size();
     for (size_t i = 0; i < numMonitors; i++) {
         const Monitor& monitor = monitors[i];
@@ -5689,8 +5687,9 @@
         std::scoped_lock _l(mLock);
         const sp<IBinder>& token = serverChannel->getConnectionToken();
         int fd = serverChannel->getFd();
-        sp<Connection> connection =
-                sp<Connection>::make(std::move(serverChannel), /*monitor=*/false, mIdGenerator);
+        std::shared_ptr<Connection> connection =
+                std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false,
+                                             mIdGenerator);
 
         if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
             ALOGE("Created a new connection, but the token %p is already known", token.get());
@@ -5727,8 +5726,8 @@
                                           << " without a specified display.";
         }
 
-        sp<Connection> connection =
-                sp<Connection>::make(serverChannel, /*monitor=*/true, mIdGenerator);
+        std::shared_ptr<Connection> connection =
+                std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator);
         const sp<IBinder>& token = serverChannel->getConnectionToken();
         const int fd = serverChannel->getFd();
 
@@ -5768,7 +5767,7 @@
 
 status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
                                                    bool notify) {
-    sp<Connection> connection = getConnectionLocked(connectionToken);
+    std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
     if (connection == nullptr) {
         // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
         return BAD_VALUE;
@@ -5914,7 +5913,8 @@
     return std::nullopt;
 }
 
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
+std::shared_ptr<Connection> InputDispatcher::getConnectionLocked(
+        const sp<IBinder>& inputConnectionToken) const {
     if (inputConnectionToken == nullptr) {
         return nullptr;
     }
@@ -5929,21 +5929,22 @@
 }
 
 std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
-    sp<Connection> connection = getConnectionLocked(connectionToken);
+    std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
     if (connection == nullptr) {
         return "<nullptr>";
     }
     return connection->getInputChannelName();
 }
 
-void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) {
     mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
     mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
 }
 
 void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
-                                                     const sp<Connection>& connection, uint32_t seq,
-                                                     bool handled, nsecs_t consumeTime) {
+                                                     const std::shared_ptr<Connection>& connection,
+                                                     uint32_t seq, bool handled,
+                                                     nsecs_t consumeTime) {
     // Handle post-event policy actions.
     std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
     if (dispatchEntryIt == connection->waitQueue.end()) {
@@ -6021,7 +6022,7 @@
     postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {
     if (connection == nullptr) {
         LOG_ALWAYS_FATAL("Caller must check for nullness");
     }
@@ -6183,9 +6184,9 @@
     sendWindowResponsiveCommandLocked(connectionToken, pid);
 }
 
-bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
-                                                       DispatchEntry* dispatchEntry,
-                                                       KeyEntry& keyEntry, bool handled) {
+bool InputDispatcher::afterKeyEventLockedInterruptable(
+        const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+        KeyEntry& keyEntry, bool handled) {
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
@@ -6197,7 +6198,7 @@
     // Get the fallback key state.
     // Clear it out after dispatching the UP.
     int32_t originalKeyCode = keyEntry.keyCode;
-    int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
+    std::optional<int32_t> fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
     if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
         connection->inputState.removeFallbackKey(originalKeyCode);
     }
@@ -6206,7 +6207,7 @@
         // If the application handles the original key for which we previously
         // generated a fallback or if the window is not a foreground window,
         // then cancel the associated fallback key, if any.
-        if (fallbackKeyCode != -1) {
+        if (fallbackKeyCode) {
             // Dispatch the unhandled key to the policy with the cancel flag.
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
@@ -6225,12 +6226,12 @@
             mLock.lock();
 
             // Cancel the fallback key.
-            if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
+            if (*fallbackKeyCode != AKEYCODE_UNKNOWN) {
                 CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
                                            "application handled the original non-fallback key "
                                            "or is no longer a foreground target, "
                                            "canceling previously dispatched fallback key");
-                options.keyCode = fallbackKeyCode;
+                options.keyCode = *fallbackKeyCode;
                 synthesizeCancelationEventsForConnectionLocked(connection, options);
             }
             connection->inputState.removeFallbackKey(originalKeyCode);
@@ -6240,7 +6241,7 @@
         // that we are in a good state to perform unhandled key event processing
         // Then ask the policy what to do with it.
         bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
-        if (fallbackKeyCode == -1 && !initialDown) {
+        if (!fallbackKeyCode && !initialDown) {
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 ALOGD("Unhandled key event: Skipping unhandled key event processing "
                       "since this is not an initial down.  "
@@ -6275,53 +6276,53 @@
         // The fallback keycode cannot change at any other point in the lifecycle.
         if (initialDown) {
             if (fallback) {
-                fallbackKeyCode = event.getKeyCode();
+                *fallbackKeyCode = event.getKeyCode();
             } else {
-                fallbackKeyCode = AKEYCODE_UNKNOWN;
+                *fallbackKeyCode = AKEYCODE_UNKNOWN;
             }
-            connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+            connection->inputState.setFallbackKey(originalKeyCode, *fallbackKeyCode);
         }
 
-        ALOG_ASSERT(fallbackKeyCode != -1);
+        ALOG_ASSERT(fallbackKeyCode);
 
         // Cancel the fallback key if the policy decides not to send it anymore.
         // We will continue to dispatch the key to the policy but we will no
         // longer dispatch a fallback key to the application.
-        if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
-            (!fallback || fallbackKeyCode != event.getKeyCode())) {
+        if (*fallbackKeyCode != AKEYCODE_UNKNOWN &&
+            (!fallback || *fallbackKeyCode != event.getKeyCode())) {
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 if (fallback) {
                     ALOGD("Unhandled key event: Policy requested to send key %d"
                           "as a fallback for %d, but on the DOWN it had requested "
                           "to send %d instead.  Fallback canceled.",
-                          event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+                          event.getKeyCode(), originalKeyCode, *fallbackKeyCode);
                 } else {
                     ALOGD("Unhandled key event: Policy did not request fallback for %d, "
                           "but on the DOWN it had requested to send %d.  "
                           "Fallback canceled.",
-                          originalKeyCode, fallbackKeyCode);
+                          originalKeyCode, *fallbackKeyCode);
                 }
             }
 
             CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
                                        "canceling fallback, policy no longer desires it");
-            options.keyCode = fallbackKeyCode;
+            options.keyCode = *fallbackKeyCode;
             synthesizeCancelationEventsForConnectionLocked(connection, options);
 
             fallback = false;
-            fallbackKeyCode = AKEYCODE_UNKNOWN;
+            *fallbackKeyCode = AKEYCODE_UNKNOWN;
             if (keyEntry.action != AKEY_EVENT_ACTION_UP) {
-                connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+                connection->inputState.setFallbackKey(originalKeyCode, *fallbackKeyCode);
             }
         }
 
         if (DEBUG_OUTBOUND_EVENT_DETAILS) {
             {
                 std::string msg;
-                const KeyedVector<int32_t, int32_t>& fallbackKeys =
+                const std::map<int32_t, int32_t>& fallbackKeys =
                         connection->inputState.getFallbackKeys();
-                for (size_t i = 0; i < fallbackKeys.size(); i++) {
-                    msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+                for (const auto& [key, value] : fallbackKeys) {
+                    msg += StringPrintf(", %d->%d", key, value);
                 }
                 ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
                       fallbackKeys.size(), msg.c_str());
@@ -6335,7 +6336,7 @@
             keyEntry.source = event.getSource();
             keyEntry.displayId = event.getDisplayId();
             keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
-            keyEntry.keyCode = fallbackKeyCode;
+            keyEntry.keyCode = *fallbackKeyCode;
             keyEntry.scanCode = event.getScanCode();
             keyEntry.metaState = event.getMetaState();
             keyEntry.repeatCount = event.getRepeatCount();
@@ -6345,7 +6346,7 @@
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 ALOGD("Unhandled key event: Dispatching fallback key.  "
                       "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                      originalKeyCode, fallbackKeyCode, keyEntry.metaState);
+                      originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
             }
             return true; // restart the event
         } else {
@@ -6360,9 +6361,9 @@
     return false;
 }
 
-bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
-                                                          DispatchEntry* dispatchEntry,
-                                                          MotionEntry& motionEntry, bool handled) {
+bool InputDispatcher::afterMotionEventLockedInterruptable(
+        const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+        MotionEntry& motionEntry, bool handled) {
     return false;
 }
 
@@ -6684,9 +6685,11 @@
         wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
                 InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
         state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds, downTimeInTarget);
-        sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken());
+        std::shared_ptr<Connection> wallpaperConnection =
+                getConnectionLocked(newWallpaper->getToken());
         if (wallpaperConnection != nullptr) {
-            sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken());
+            std::shared_ptr<Connection> toConnection =
+                    getConnectionLocked(toWindowHandle->getToken());
             toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
             synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection,
                                                            wallpaperFlags);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index aaf1214..7aa1a2d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -94,14 +94,14 @@
     status_t stop() override;
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override{};
-    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    void notifyKey(const NotifyKeyArgs* args) override;
-    void notifyMotion(const NotifyMotionArgs* args) override;
-    void notifySwitch(const NotifySwitchArgs* args) override;
-    void notifySensor(const NotifySensorArgs* args) override;
-    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+    void notifyKey(const NotifyKeyArgs& args) override;
+    void notifyMotion(const NotifyMotionArgs& args) override;
+    void notifySwitch(const NotifySwitchArgs& args) override;
+    void notifySensor(const NotifySensorArgs& args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
     android::os::InputEventInjectionResult injectInputEvent(
             const InputEvent* event, std::optional<int32_t> targetUid,
@@ -232,7 +232,7 @@
     nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
 
     bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
-    bool isAppSwitchPendingLocked() REQUIRES(mLock);
+    bool isAppSwitchPendingLocked() const REQUIRES(mLock);
     void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
 
     // Blocked event latency optimization.  Drops old events when the user intends
@@ -249,12 +249,12 @@
     sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
             REQUIRES(mLock);
 
-    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+    std::shared_ptr<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
 
     std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
 
-    void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+    void removeConnectionLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
 
     status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
@@ -264,8 +264,8 @@
     };
 
     // All registered connections mapped by input channel token.
-    std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
-            GUARDED_BY(mLock);
+    std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>>
+            mConnectionsByToken GUARDED_BY(mLock);
 
     // Find a monitor pid by the provided token.
     std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -328,12 +328,12 @@
     std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
 
     nsecs_t processAnrsLocked() REQUIRES(mLock);
-    std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<Connection>& connection)
-            REQUIRES(mLock);
+    std::chrono::nanoseconds getDispatchingTimeoutLocked(
+            const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
 
     // Input filter processing.
-    bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
-    bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+    bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs& args) REQUIRES(mLock);
+    bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
 
     // Inbound event processing.
     void drainInboundQueueLocked() REQUIRES(mLock);
@@ -533,7 +533,7 @@
     // prevent unneeded wakeups.
     AnrTracker mAnrTracker GUARDED_BY(mLock);
 
-    void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+    void cancelEventsForAnrLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
     // If a focused application changes, we should stop counting down the "no focused window" time,
     // because we will have no way of knowing when the previous application actually added a window.
     // This also means that we will miss cases like pulling down notification shade when the
@@ -594,22 +594,26 @@
     // These methods are deliberately not Interruptible because doing all of the work
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
     // If needed, the methods post commands to run later once the critical bits are done.
-    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+    void prepareDispatchCycleLocked(nsecs_t currentTime,
+                                    const std::shared_ptr<Connection>& connection,
                                     std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
             REQUIRES(mLock);
-    void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
+    void enqueueDispatchEntriesLocked(nsecs_t currentTime,
+                                      const std::shared_ptr<Connection>& connection,
                                       std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
             REQUIRES(mLock);
-    void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
-                                    const InputTarget& inputTarget,
+    void enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
+                                    std::shared_ptr<EventEntry>, const InputTarget& inputTarget,
                                     ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock);
     status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
-    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+    void startDispatchCycleLocked(nsecs_t currentTime,
+                                  const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
+    void finishDispatchCycleLocked(nsecs_t currentTime,
+                                   const std::shared_ptr<Connection>& connection, uint32_t seq,
+                                   bool handled, nsecs_t consumeTime) REQUIRES(mLock);
+    void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
+                                        const std::shared_ptr<Connection>& connection, bool notify)
             REQUIRES(mLock);
-    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                   uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock);
-    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                        bool notify) REQUIRES(mLock);
     void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
     void releaseDispatchEntry(DispatchEntry* dispatchEntry);
     int handleReceiveCallback(int events, sp<IBinder> connectionToken);
@@ -624,14 +628,13 @@
     void synthesizeCancelationEventsForInputChannelLocked(
             const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
             REQUIRES(mLock);
-    void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
-                                                        const CancelationOptions& options)
+    void synthesizeCancelationEventsForConnectionLocked(
+            const std::shared_ptr<Connection>& connection, const CancelationOptions& options)
             REQUIRES(mLock);
 
-    void synthesizePointerDownEventsForConnectionLocked(const nsecs_t downTime,
-                                                        const sp<Connection>& connection,
-                                                        ftl::Flags<InputTarget::Flags> targetFlags)
-            REQUIRES(mLock);
+    void synthesizePointerDownEventsForConnectionLocked(
+            const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
+            ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock);
 
     void synthesizeCancelationEventsForWindowLocked(
             const sp<android::gui::WindowInfoHandle>& windowHandle,
@@ -647,10 +650,10 @@
     void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
 
     // Dump state.
-    void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
-    void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
-    void logDispatchStateLocked() REQUIRES(mLock);
-    std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
+    void dumpDispatchStateLocked(std::string& dump) const REQUIRES(mLock);
+    void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const;
+    void logDispatchStateLocked() const REQUIRES(mLock);
+    std::string dumpPointerCaptureStateLocked() const REQUIRES(mLock);
 
     // Registration.
     void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
@@ -658,16 +661,16 @@
             REQUIRES(mLock);
 
     // Interesting events that we might like to log or tell the framework about.
-    void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
-                                        uint32_t seq, bool handled, nsecs_t consumeTime)
-            REQUIRES(mLock);
+    void doDispatchCycleFinishedCommand(nsecs_t finishTime,
+                                        const std::shared_ptr<Connection>& connection, uint32_t seq,
+                                        bool handled, nsecs_t consumeTime) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
                                                 KeyEntry& entry) REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
     void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
             REQUIRES(mLock);
     void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
-    void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+    void onAnrLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
                                   const std::string& reason) REQUIRES(mLock);
@@ -675,10 +678,10 @@
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
             REQUIRES(mLock);
-    bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
+    bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
                                           DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
+    bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
 
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 94f3813..4652c2d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -42,13 +42,8 @@
     switch (action) {
         case AKEY_EVENT_ACTION_UP: {
             if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
-                for (size_t i = 0; i < mFallbackKeys.size();) {
-                    if (mFallbackKeys.valueAt(i) == entry.keyCode) {
-                        mFallbackKeys.removeItemsAt(i);
-                    } else {
-                        i += 1;
-                    }
-                }
+                std::erase_if(mFallbackKeys,
+                              [&entry](const auto& item) { return item.second == entry.keyCode; });
             }
             ssize_t index = findKeyMemento(entry);
             if (index >= 0) {
@@ -481,22 +476,20 @@
     }
 }
 
-int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
-    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
-    return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
+std::optional<int32_t> InputState::getFallbackKey(int32_t originalKeyCode) {
+    auto it = mFallbackKeys.find(originalKeyCode);
+    if (it == mFallbackKeys.end()) {
+        return {};
+    }
+    return it->second;
 }
 
 void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) {
-    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
-    if (index >= 0) {
-        mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
-    } else {
-        mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
-    }
+    mFallbackKeys.insert_or_assign(originalKeyCode, fallbackKeyCode);
 }
 
 void InputState::removeFallbackKey(int32_t originalKeyCode) {
-    mFallbackKeys.removeItem(originalKeyCode);
+    mFallbackKeys.erase(originalKeyCode);
 }
 
 bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) {
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d788e47..af2a3cb 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -62,9 +62,9 @@
     void mergePointerStateTo(InputState& other);
 
     // Gets the fallback key associated with a keycode.
-    // Returns -1 if none.
+    // Returns std::nullopt if none.
     // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
-    int32_t getFallbackKey(int32_t originalKeyCode);
+    std::optional<int32_t> getFallbackKey(int32_t originalKeyCode);
 
     // Sets the fallback key for a particular keycode.
     void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
@@ -72,7 +72,7 @@
     // Removes the fallback key for a particular keycode.
     void removeFallbackKey(int32_t originalKeyCode);
 
-    inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const { return mFallbackKeys; }
+    inline const std::map<int32_t, int32_t>& getFallbackKeys() const { return mFallbackKeys; }
 
 private:
     struct KeyMemento {
@@ -113,7 +113,7 @@
 
     std::vector<KeyMemento> mKeyMementos;
     std::vector<MotionMemento> mMotionMementos;
-    KeyedVector<int32_t, int32_t> mFallbackKeys;
+    std::map</*originalKeyCode*/int32_t, /*fallbackKeyCode*/int32_t> mFallbackKeys;
 
     ssize_t findKeyMemento(const KeyEntry& entry) const;
     ssize_t findMotionMemento(const MotionEntry& entry, bool hovering) const;
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp
index a5bfc25..96d78c3 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp
@@ -256,7 +256,7 @@
     }
 }
 
-std::string LatencyAggregator::dump(const char* prefix) {
+std::string LatencyAggregator::dump(const char* prefix) const {
     std::string sketchDump = StringPrintf("%s  Sketches:\n", prefix);
     for (size_t i = 0; i < SketchIndex::SIZE; i++) {
         const int64_t numDown = mDownSketches[i]->num_values();
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
index accfc29..60b6813 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.h
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -56,7 +56,7 @@
      */
     void processTimeline(const InputEventTimeline& timeline) override;
 
-    std::string dump(const char* prefix);
+    std::string dump(const char* prefix) const;
 
     ~LatencyAggregator();
 
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 2dd7d3f..b7c36a8 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -165,7 +165,7 @@
     }
 }
 
-std::string LatencyTracker::dump(const char* prefix) {
+std::string LatencyTracker::dump(const char* prefix) const {
     return StringPrintf("%sLatencyTracker:\n", prefix) +
             StringPrintf("%s  mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
             StringPrintf("%s  mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 64dfeef..4212da8 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -55,7 +55,7 @@
     void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
-    std::string dump(const char* prefix);
+    std::string dump(const char* prefix) const;
 
 private:
     /**
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index d1b86c8..4f78f03 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -38,14 +38,14 @@
     virtual ~InputListenerInterface() { }
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) = 0;
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
-    virtual void notifyKey(const NotifyKeyArgs* args) = 0;
-    virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
-    virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
-    virtual void notifySensor(const NotifySensorArgs* args) = 0;
-    virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) = 0;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
-    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) = 0;
+    virtual void notifyKey(const NotifyKeyArgs& args) = 0;
+    virtual void notifyMotion(const NotifyMotionArgs& args) = 0;
+    virtual void notifySwitch(const NotifySwitchArgs& args) = 0;
+    virtual void notifySensor(const NotifySensorArgs& args) = 0;
+    virtual void notifyVibratorState(const NotifyVibratorStateArgs& args) = 0;
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) = 0;
+    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) = 0;
 
     void notify(const NotifyArgs& args);
 };
@@ -60,14 +60,14 @@
     explicit QueuedInputListener(InputListenerInterface& innerListener);
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
-    virtual void notifySensor(const NotifySensorArgs* args) override;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+    virtual void notifyKey(const NotifyKeyArgs& args) override;
+    virtual void notifyMotion(const NotifyMotionArgs& args) override;
+    virtual void notifySwitch(const NotifySwitchArgs& args) override;
+    virtual void notifySensor(const NotifySensorArgs& args) override;
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
     void flush();
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 1750c64..a93a2ea 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -42,118 +42,6 @@
 
 namespace android {
 
-// --- InputReaderInterface ---
-
-/* The interface for the InputReader shared library.
- *
- * Manages one or more threads that process raw input events and sends cooked event data to an
- * input listener.
- *
- * The implementation must guarantee thread safety for this interface. However, since the input
- * listener is NOT thread safe, all calls to the listener must happen from the same thread.
- */
-class InputReaderInterface {
-public:
-    InputReaderInterface() { }
-    virtual ~InputReaderInterface() { }
-
-    /* Dumps the state of the input reader.
-     *
-     * This method may be called on any thread (usually by the input manager). */
-    virtual void dump(std::string& dump) = 0;
-
-    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
-    virtual void monitor() = 0;
-
-    /* Returns true if the input device is enabled. */
-    virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
-
-    /* Makes the reader start processing events from the kernel. */
-    virtual status_t start() = 0;
-
-    /* Makes the reader stop processing any more events. */
-    virtual status_t stop() = 0;
-
-    /* Gets information about all input devices.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
-
-    /* Query current input state. */
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode) = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode) = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw) = 0;
-
-    virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
-                                 int32_t toKeyCode) const = 0;
-
-    virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
-
-    /* Toggle Caps Lock */
-    virtual void toggleCapsLockState(int32_t deviceId) = 0;
-
-    /* Determine whether physical keys exist for the given framework-domain key codes. */
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-                         const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0;
-
-    /* Requests that a reconfiguration of all input devices.
-     * The changes flag is a bitfield that indicates what has changed and whether
-     * the input devices must all be reopened. */
-    virtual void requestRefreshConfiguration(uint32_t changes) = 0;
-
-    /* Controls the vibrator of a particular input device. */
-    virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
-                         int32_t token) = 0;
-    virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
-
-    virtual bool isVibrating(int32_t deviceId) = 0;
-
-    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
-    /* Get battery level of a particular input device. */
-    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
-    /* Get battery status of a particular input device. */
-    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
-    /* Get the device path for the battery of an input device. */
-    virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
-
-    virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
-
-    virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
-
-    /* Return true if the device can send input events to the specified display. */
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
-
-    /* Enable sensor in input reader mapper. */
-    virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
-                              std::chrono::microseconds samplingPeriod,
-                              std::chrono::microseconds maxBatchReportLatency) = 0;
-
-    /* Disable sensor in input reader mapper. */
-    virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
-
-    /* Flush sensor data in input reader mapper. */
-    virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
-
-    /* Set color for the light */
-    virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0;
-    /* Set player ID for the light */
-    virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0;
-    /* Get light color */
-    virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
-    /* Get light player ID */
-    virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
-
-    /* Get the Bluetooth address of an input device, if known. */
-    virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
-
-    /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
-    virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
-};
-
 // --- InputReaderConfiguration ---
 
 /*
@@ -163,51 +51,51 @@
  */
 struct InputReaderConfiguration {
     // Describes changes that have occurred.
-    enum {
+    enum class Change : uint32_t {
         // The mouse pointer speed changed.
-        CHANGE_POINTER_SPEED = 1 << 0,
+        POINTER_SPEED = 1u << 0,
 
         // The pointer gesture control changed.
-        CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
+        POINTER_GESTURE_ENABLEMENT = 1u << 1,
 
         // The display size or orientation changed.
-        CHANGE_DISPLAY_INFO = 1 << 2,
+        DISPLAY_INFO = 1u << 2,
 
         // The visible touches option changed.
-        CHANGE_SHOW_TOUCHES = 1 << 3,
+        SHOW_TOUCHES = 1u << 3,
 
         // The keyboard layouts must be reloaded.
-        CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
+        KEYBOARD_LAYOUTS = 1u << 4,
 
         // The device name alias supplied by the may have changed for some devices.
-        CHANGE_DEVICE_ALIAS = 1 << 5,
+        DEVICE_ALIAS = 1u << 5,
 
         // The location calibration matrix changed.
-        CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
+        TOUCH_AFFINE_TRANSFORMATION = 1u << 6,
 
         // The presence of an external stylus has changed.
-        CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
+        EXTERNAL_STYLUS_PRESENCE = 1u << 7,
 
         // The pointer capture mode has changed.
-        CHANGE_POINTER_CAPTURE = 1 << 8,
+        POINTER_CAPTURE = 1u << 8,
 
         // The set of disabled input devices (disabledDevices) has changed.
-        CHANGE_ENABLED_STATE = 1 << 9,
+        ENABLED_STATE = 1u << 9,
 
         // The device type has been updated.
-        CHANGE_DEVICE_TYPE = 1 << 10,
+        DEVICE_TYPE = 1u << 10,
 
         // The keyboard layout association has changed.
-        CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11,
+        KEYBOARD_LAYOUT_ASSOCIATION = 1u << 11,
 
         // The stylus button reporting configurations has changed.
-        CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
+        STYLUS_BUTTON_REPORTING = 1u << 12,
 
         // The touchpad settings changed.
-        CHANGE_TOUCHPAD_SETTINGS = 1 << 13,
+        TOUCHPAD_SETTINGS = 1u << 13,
 
         // All devices must be reopened.
-        CHANGE_MUST_REOPEN = 1 << 31,
+        MUST_REOPEN = 1u << 31,
     };
 
     // Gets the amount of time to disable virtual keys after the screen is touched
@@ -367,8 +255,6 @@
             stylusButtonMotionEventsEnabled(true),
             stylusPointerIconEnabled(false) {}
 
-    static std::string changesToString(uint32_t changes);
-
     std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
             const;
@@ -383,6 +269,116 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
+using ConfigurationChanges = ftl::Flags<InputReaderConfiguration::Change>;
+
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
+class InputReaderInterface {
+public:
+    InputReaderInterface() {}
+    virtual ~InputReaderInterface() {}
+    /* Dumps the state of the input reader.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(std::string& dump) = 0;
+
+    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
+    virtual void monitor() = 0;
+
+    /* Returns true if the input device is enabled. */
+    virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
+
+    /* Makes the reader start processing events from the kernel. */
+    virtual status_t start() = 0;
+
+    /* Makes the reader stop processing any more events. */
+    virtual status_t stop() = 0;
+
+    /* Gets information about all input devices.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
+
+    /* Query current input state. */
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) = 0;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0;
+
+    virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+                                 int32_t toKeyCode) const = 0;
+
+    virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
+
+    /* Toggle Caps Lock */
+    virtual void toggleCapsLockState(int32_t deviceId) = 0;
+
+    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+                         const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0;
+
+    /* Requests that a reconfiguration of all input devices.
+     * The changes flag is a bitfield that indicates what has changed and whether
+     * the input devices must all be reopened. */
+    virtual void requestRefreshConfiguration(ConfigurationChanges changes) = 0;
+
+    /* Controls the vibrator of a particular input device. */
+    virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+                         int32_t token) = 0;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+    virtual bool isVibrating(int32_t deviceId) = 0;
+
+    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+    /* Get battery level of a particular input device. */
+    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
+    /* Get battery status of a particular input device. */
+    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
+    /* Get the device path for the battery of an input device. */
+    virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
+
+    virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
+
+    virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
+
+    /* Return true if the device can send input events to the specified display. */
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+
+    /* Enable sensor in input reader mapper. */
+    virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                              std::chrono::microseconds samplingPeriod,
+                              std::chrono::microseconds maxBatchReportLatency) = 0;
+
+    /* Disable sensor in input reader mapper. */
+    virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+    /* Flush sensor data in input reader mapper. */
+    virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+    /* Set color for the light */
+    virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0;
+    /* Set player ID for the light */
+    virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0;
+    /* Get light color */
+    virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
+    /* Get light player ID */
+    virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+    /* Get the Bluetooth address of an input device, if known. */
+    virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
+
+    /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
+    virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
+};
+
 // --- TouchAffineTransformation ---
 
 struct TouchAffineTransformation {
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index f12482b..7d29dd9 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -36,6 +36,7 @@
     bool operator==(const NotifyInputDevicesChangedArgs& rhs) const = default;
 
     NotifyInputDevicesChangedArgs(const NotifyInputDevicesChangedArgs& other) = default;
+    NotifyInputDevicesChangedArgs& operator=(const NotifyInputDevicesChangedArgs&) = default;
 };
 
 /* Describes a configuration change event. */
@@ -50,6 +51,7 @@
     bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
 
     NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
+    NotifyConfigurationChangedArgs& operator=(const NotifyConfigurationChangedArgs&) = default;
 };
 
 /* Describes a key event. */
@@ -79,6 +81,7 @@
     bool operator==(const NotifyKeyArgs& rhs) const = default;
 
     NotifyKeyArgs(const NotifyKeyArgs& other) = default;
+    NotifyKeyArgs& operator=(const NotifyKeyArgs&) = default;
 };
 
 /* Describes a motion event. */
@@ -129,7 +132,6 @@
                      const std::vector<TouchVideoFrame>& videoFrames);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
-
     NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
 
     bool operator==(const NotifyMotionArgs& rhs) const;
@@ -157,6 +159,7 @@
                      bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
 
     NotifySensorArgs(const NotifySensorArgs& other) = default;
+    NotifySensorArgs& operator=(const NotifySensorArgs&) = default;
 };
 
 /* Describes a switch event. */
@@ -174,6 +177,7 @@
                      uint32_t switchMask);
 
     NotifySwitchArgs(const NotifySwitchArgs& other) = default;
+    NotifySwitchArgs& operator=(const NotifySwitchArgs&) = default;
 
     bool operator==(const NotifySwitchArgs& rhs) const = default;
 };
@@ -191,6 +195,7 @@
     NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
 
     NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default;
+    NotifyDeviceResetArgs& operator=(const NotifyDeviceResetArgs&) = default;
 
     bool operator==(const NotifyDeviceResetArgs& rhs) const = default;
 };
@@ -207,6 +212,7 @@
     NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
 
     NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default;
+    NotifyPointerCaptureChangedArgs& operator=(const NotifyPointerCaptureChangedArgs&) = default;
 };
 
 /* Describes a vibrator state event. */
@@ -222,6 +228,7 @@
     NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
 
     NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) = default;
+    NotifyVibratorStateArgs& operator=(const NotifyVibratorStateArgs&) = default;
 };
 
 using NotifyArgs =
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ccf4118..b86906b 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -146,98 +146,23 @@
     }
 }
 
-void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
+void InputDevice::addEmptyEventHubDevice(int32_t eventHubId) {
     if (mDevices.find(eventHubId) != mDevices.end()) {
         return;
     }
     std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
-    ftl::Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();
     std::vector<std::unique_ptr<InputMapper>> mappers;
 
-    // Check if we should skip population
-    if (!populateMappers) {
-        mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+}
+
+void InputDevice::addEventHubDevice(int32_t eventHubId,
+                                    const InputReaderConfiguration& readerConfig) {
+    if (mDevices.find(eventHubId) != mDevices.end()) {
         return;
     }
-
-    // Switch-like devices.
-    if (classes.test(InputDeviceClass::SWITCH)) {
-        mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));
-    }
-
-    // Scroll wheel-like devices.
-    if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
-        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));
-    }
-
-    // Vibrator-like devices.
-    if (classes.test(InputDeviceClass::VIBRATOR)) {
-        mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
-    }
-
-    // Battery-like devices or light-containing devices.
-    // PeripheralController will be created with associated EventHub device.
-    if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
-        mController = std::make_unique<PeripheralController>(*contextPtr);
-    }
-
-    // Keyboard-like devices.
-    uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes.test(InputDeviceClass::KEYBOARD)) {
-        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
-    }
-    if (classes.test(InputDeviceClass::ALPHAKEY)) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
-    if (classes.test(InputDeviceClass::DPAD)) {
-        keyboardSource |= AINPUT_SOURCE_DPAD;
-    }
-    if (classes.test(InputDeviceClass::GAMEPAD)) {
-        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
-    }
-
-    if (keyboardSource != 0) {
-        mappers.push_back(
-                std::make_unique<KeyboardInputMapper>(*contextPtr, keyboardSource, keyboardType));
-    }
-
-    // Cursor-like devices.
-    if (classes.test(InputDeviceClass::CURSOR)) {
-        mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));
-    }
-
-    // Touchscreens and touchpad devices.
-    static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
-            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
-    // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
-    // at least load this setting from the IDC file.
-    const InputDeviceIdentifier identifier = contextPtr->getDeviceIdentifier();
-    const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
-            (identifier.product == 0x05c4 || identifier.product == 0x09cc);
-    if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
-        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
-        mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
-    } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
-        mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
-    } else if (classes.test(InputDeviceClass::TOUCH)) {
-        mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
-    }
-
-    // Joystick-like devices.
-    if (classes.test(InputDeviceClass::JOYSTICK)) {
-        mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
-    }
-
-    // Motion sensor enabled devices.
-    if (classes.test(InputDeviceClass::SENSOR)) {
-        mappers.push_back(std::make_unique<SensorInputMapper>(*contextPtr));
-    }
-
-    // External stylus-like devices.
-    if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
-        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
-    }
+    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
+    std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
 
     // insert the context into the devices set
     mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
@@ -255,7 +180,7 @@
 
 std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
                                              const InputReaderConfiguration& readerConfig,
-                                             uint32_t changes) {
+                                             ConfigurationChanges changes) {
     std::list<NotifyArgs> out;
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
@@ -276,12 +201,14 @@
     mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
     mHasMic = mClasses.test(InputDeviceClass::MIC);
 
+    using Change = InputReaderConfiguration::Change;
+
     if (!isIgnored()) {
         // Full configuration should happen the first time configure is called
         // and when the device type is changed. Changing a device type can
         // affect various other parameters so should result in a
         // reconfiguration.
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
+        if (!changes.any() || changes.test(Change::DEVICE_TYPE)) {
             mConfiguration.clear();
             for_each_subdevice([this](InputDeviceContext& context) {
                 std::optional<PropertyMap> configuration =
@@ -295,7 +222,7 @@
                     getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
         }
 
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
+        if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
             if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
                 std::shared_ptr<KeyCharacterMap> keyboardLayout =
                         mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
@@ -312,7 +239,7 @@
             }
         }
 
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
+        if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
             if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
                 std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
                 if (mAlias != alias) {
@@ -322,7 +249,7 @@
             }
         }
 
-        if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) {
+        if (changes.test(Change::ENABLED_STATE)) {
             // Do not execute this code on the first configure, because 'setEnabled' would call
             // InputMapper::reset, and you can't reset a mapper before it has been configured.
             // The mappers are configured for the first time at the bottom of this function.
@@ -331,7 +258,7 @@
             out += setEnabled(enabled, when);
         }
 
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
             // In most situations, no port or name will be specified.
             mAssociatedDisplayPort = std::nullopt;
             mAssociatedDisplayUniqueId = std::nullopt;
@@ -380,7 +307,7 @@
                 }
             }
 
-            if (changes) {
+            if (changes.any()) {
                 // For first-time configuration, only allow device to be disabled after mappers have
                 // finished configuring. This is because we need to read some of the properties from
                 // the device's open fd.
@@ -395,7 +322,7 @@
 
         // If a device is just plugged but it might be disabled, we need to update some info like
         // axis range of touch from each InputMapper first, then disable it.
-        if (!changes) {
+        if (!changes.any()) {
             out += setEnabled(readerConfig.disabledDevices.find(mId) ==
                                       readerConfig.disabledDevices.end(),
                               when);
@@ -512,6 +439,92 @@
     return result;
 }
 
+std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers(
+        InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig) {
+    ftl::Flags<InputDeviceClass> classes = contextPtr.getDeviceClasses();
+    std::vector<std::unique_ptr<InputMapper>> mappers;
+
+    // Switch-like devices.
+    if (classes.test(InputDeviceClass::SWITCH)) {
+        mappers.push_back(std::make_unique<SwitchInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Scroll wheel-like devices.
+    if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
+        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Vibrator-like devices.
+    if (classes.test(InputDeviceClass::VIBRATOR)) {
+        mappers.push_back(std::make_unique<VibratorInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Battery-like devices or light-containing devices.
+    // PeripheralController will be created with associated EventHub device.
+    if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
+        mController = std::make_unique<PeripheralController>(contextPtr);
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSource = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes.test(InputDeviceClass::KEYBOARD)) {
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes.test(InputDeviceClass::ALPHAKEY)) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes.test(InputDeviceClass::DPAD)) {
+        keyboardSource |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes.test(InputDeviceClass::GAMEPAD)) {
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSource != 0) {
+        mappers.push_back(std::make_unique<KeyboardInputMapper>(contextPtr, readerConfig,
+                                                                keyboardSource, keyboardType));
+    }
+
+    // Cursor-like devices.
+    if (classes.test(InputDeviceClass::CURSOR)) {
+        mappers.push_back(std::make_unique<CursorInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Touchscreens and touchpad devices.
+    static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
+    // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
+    // at least load this setting from the IDC file.
+    const InputDeviceIdentifier identifier = contextPtr.getDeviceIdentifier();
+    const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
+            (identifier.product == 0x05c4 || identifier.product == 0x09cc);
+    if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
+        mappers.push_back(std::make_unique<TouchpadInputMapper>(contextPtr, readerConfig));
+    } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
+    } else if (classes.test(InputDeviceClass::TOUCH)) {
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Joystick-like devices.
+    if (classes.test(InputDeviceClass::JOYSTICK)) {
+        mappers.push_back(std::make_unique<JoystickInputMapper>(contextPtr, readerConfig));
+    }
+
+    // Motion sensor enabled devices.
+    if (classes.test(InputDeviceClass::SENSOR)) {
+        mappers.push_back(std::make_unique<SensorInputMapper>(contextPtr, readerConfig));
+    }
+
+    // External stylus-like devices.
+    if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
+        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(contextPtr, readerConfig));
+    }
+    return mappers;
+}
+
 bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                         uint8_t* outFlags) {
     bool result = false;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 16b365f..ea95f78 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -85,7 +85,7 @@
         mDisableVirtualKeysTimeout(LLONG_MIN),
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
-    refreshConfigurationLocked(0);
+    refreshConfigurationLocked(/*changes=*/{});
     updateGlobalMetaStateLocked();
 }
 
@@ -122,9 +122,9 @@
         oldGeneration = mGeneration;
         timeoutMillis = -1;
 
-        uint32_t changes = mConfigurationChangesToRefresh;
-        if (changes) {
-            mConfigurationChangesToRefresh = 0;
+        auto changes = mConfigurationChangesToRefresh;
+        if (changes.any()) {
+            mConfigurationChangesToRefresh.clear();
             timeoutMillis = 0;
             refreshConfigurationLocked(changes);
         } else if (mNextTimeout != LLONG_MAX) {
@@ -236,7 +236,7 @@
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
     std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
 
-    notifyAll(device->configure(when, mConfig, 0));
+    notifyAll(device->configure(when, mConfig, /*changes=*/{}));
     notifyAll(device->reset(when));
 
     if (device->isIgnored()) {
@@ -312,7 +312,7 @@
 
     std::list<NotifyArgs> resetEvents;
     if (device->hasEventHubDevices()) {
-        resetEvents += device->configure(when, mConfig, 0);
+        resetEvents += device->configure(when, mConfig, /*changes=*/{});
     }
     resetEvents += device->reset(when);
     notifyAll(std::move(resetEvents));
@@ -334,7 +334,7 @@
         device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                                identifier);
     }
-    device->addEventHubDevice(eventHubId);
+    device->addEventHubDevice(eventHubId, mConfig);
     return device;
 }
 
@@ -387,25 +387,24 @@
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
-    mQueuedListener.notifyConfigurationChanged(&args);
+    mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
 }
 
-void InputReader::refreshConfigurationLocked(uint32_t changes) {
+void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
     mPolicy->getReaderConfiguration(&mConfig);
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
 
-    if (!changes) return;
+    using Change = InputReaderConfiguration::Change;
+    if (!changes.any()) return;
 
-    ALOGI("Reconfiguring input devices, changes=%s",
-          InputReaderConfiguration::changesToString(changes).c_str());
+    ALOGI("Reconfiguring input devices, changes=%s", changes.string().c_str());
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+    if (changes.test(Change::DISPLAY_INFO)) {
         updatePointerDisplayLocked();
     }
 
-    if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+    if (changes.test(Change::MUST_REOPEN)) {
         mEventHub->requestReopenDevices();
     } else {
         for (auto& devicePair : mDevices) {
@@ -414,15 +413,14 @@
         }
     }
 
-    if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
+    if (changes.test(Change::POINTER_CAPTURE)) {
         if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
             ALOGV("Skipping notifying pointer capture changes: "
                   "There was no change in the pointer capture state.");
         } else {
             mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
-            const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
-                                                       mCurrentPointerCaptureRequest);
-            mQueuedListener.notifyPointerCaptureChanged(&args);
+            mQueuedListener.notifyPointerCaptureChanged(
+                    {mContext.getNextId(), now, mCurrentPointerCaptureRequest});
         }
     }
 }
@@ -459,7 +457,7 @@
 }
 
 void InputReader::notifyExternalStylusPresenceChangedLocked() {
-    refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+    refreshConfigurationLocked(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
 }
 
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
@@ -673,11 +671,11 @@
     return device->getKeyCodeForKeyLocation(locationKeyCode);
 }
 
-void InputReader::requestRefreshConfiguration(uint32_t changes) {
+void InputReader::requestRefreshConfiguration(ConfigurationChanges changes) {
     std::scoped_lock _l(mLock);
 
-    if (changes) {
-        bool needWake = !mConfigurationChangesToRefresh;
+    if (changes.any()) {
+        bool needWake = !mConfigurationChangesToRefresh.any();
         mConfigurationChangesToRefresh |= changes;
 
         if (needWake) {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index ad45cb1..6303546 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -80,11 +80,12 @@
     [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump, const std::string& eventHubDevStr);
-    void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
+    void addEmptyEventHubDevice(int32_t eventHubId);
+    void addEventHubDevice(int32_t eventHubId, const InputReaderConfiguration& readerConfig);
     void removeEventHubDevice(int32_t eventHubId);
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration& readerConfig,
-                                                  uint32_t changes);
+                                                  ConfigurationChanges changes);
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
     [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
@@ -137,7 +138,7 @@
     template <class T, typename... Args>
     T& addMapper(int32_t eventHubId, Args... args) {
         // ensure a device entry exists for this eventHubId
-        addEventHubDevice(eventHubId, false);
+        addEmptyEventHubDevice(eventHubId);
 
         // create mapper
         auto& devicePair = mDevices[eventHubId];
@@ -152,7 +153,7 @@
     template <class T>
     T& addController(int32_t eventHubId) {
         // ensure a device entry exists for this eventHubId
-        addEventHubDevice(eventHubId, false);
+        addEmptyEventHubDevice(eventHubId);
 
         // create controller
         auto& devicePair = mDevices[eventHubId];
@@ -191,6 +192,9 @@
     typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
 
+    std::vector<std::unique_ptr<InputMapper>> createMappers(
+            InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig);
+
     PropertyMap mConfiguration;
 
     // helpers to interate over the devices collection
@@ -403,7 +407,7 @@
     inline const std::string getName() const { return mDevice.getName(); }
     inline const std::string getDescriptor() { return mDevice.getDescriptor(); }
     inline const std::string getLocation() { return mDevice.getLocation(); }
-    inline bool isExternal() { return mDevice.isExternal(); }
+    inline bool isExternal() const { return mDevice.isExternal(); }
     inline std::optional<uint8_t> getAssociatedDisplayPort() const {
         return mDevice.getAssociatedDisplayPort();
     }
@@ -420,7 +424,7 @@
         return mDevice.cancelTouch(when, readTime);
     }
     inline void bumpGeneration() { mDevice.bumpGeneration(); }
-    inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
+    inline const PropertyMap& getConfiguration() const { return mDevice.getConfiguration(); }
 
 private:
     InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 120e150..9112913 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -77,7 +77,7 @@
     bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                  uint8_t* outFlags) override;
 
-    void requestRefreshConfiguration(uint32_t changes) override;
+    void requestRefreshConfiguration(ConfigurationChanges changes) override;
 
     void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
                  int32_t token) override;
@@ -233,8 +233,8 @@
     nsecs_t mNextTimeout GUARDED_BY(mLock);
     void requestTimeoutAtTimeLocked(nsecs_t when) REQUIRES(mLock);
 
-    uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
-    void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
+    ConfigurationChanges mConfigurationChangesToRefresh GUARDED_BY(mLock);
+    void refreshConfigurationLocked(ConfigurationChanges changes) REQUIRES(mLock);
 
     void notifyAll(std::list<NotifyArgs>&& argsList);
 
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 1cc614e..8ef5ff6 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -68,8 +68,12 @@
 
 // --- CursorInputMapper ---
 
-CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
+CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
+                                     const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig),
+        mLastEventTime(std::numeric_limits<nsecs_t>::min()) {
+    configureWithZeroChanges(readerConfig);
+}
 
 CursorInputMapper::~CursorInputMapper() {
     if (mPointerController != nullptr) {
@@ -134,119 +138,28 @@
 }
 
 std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when,
-                                                     const InputReaderConfiguration& config,
-                                                     uint32_t changes) {
-    std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
+                                                     const InputReaderConfiguration& readerConfig,
+                                                     ConfigurationChanges changes) {
+    std::list<NotifyArgs> out = InputMapper::reconfigure(when, readerConfig, changes);
 
-    if (!changes) { // first time only
-        mCursorScrollAccumulator.configure(getDeviceContext());
-
-        // Configure basic parameters.
-        configureParameters();
-
-        // Configure device mode.
-        switch (mParameters.mode) {
-            case Parameters::Mode::POINTER_RELATIVE:
-                // Should not happen during first time configuration.
-                ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
-                mParameters.mode = Parameters::Mode::POINTER;
-                [[fallthrough]];
-            case Parameters::Mode::POINTER:
-                mSource = AINPUT_SOURCE_MOUSE;
-                mXPrecision = 1.0f;
-                mYPrecision = 1.0f;
-                mXScale = 1.0f;
-                mYScale = 1.0f;
-                mPointerController = getContext()->getPointerController(getDeviceId());
-                break;
-            case Parameters::Mode::NAVIGATION:
-                mSource = AINPUT_SOURCE_TRACKBALL;
-                mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-                mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-                mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-                mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-                break;
-        }
-
-        mVWheelScale = 1.0f;
-        mHWheelScale = 1.0f;
+    if (!changes.any()) {
+        configureWithZeroChanges(readerConfig);
+        return out;
     }
 
     const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
-            ((!changes && config.pointerCaptureRequest.enable) ||
-             (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
+            changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE);
     if (configurePointerCapture) {
-        if (config.pointerCaptureRequest.enable) {
-            if (mParameters.mode == Parameters::Mode::POINTER) {
-                mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
-                mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
-                // Keep PointerController around in order to preserve the pointer position.
-                mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
-            } else {
-                ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
-            }
-        } else {
-            if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
-                mParameters.mode = Parameters::Mode::POINTER;
-                mSource = AINPUT_SOURCE_MOUSE;
-            } else {
-                ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
-            }
-        }
-        bumpGeneration();
-        if (changes) {
-            out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
-        }
+        configureOnPointerCapture(readerConfig);
+        out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
-        configurePointerCapture) {
-        if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
-            // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
-            mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
-            mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
-            mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
-        } else {
-            mPointerVelocityControl.setParameters(config.pointerVelocityControlParameters);
-            mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
-            mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
-        }
+    if (changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || configurePointerCapture) {
+        configureOnChangePointerSpeed(readerConfig);
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
-        configurePointerCapture) {
-        const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
-
-        mDisplayId = ADISPLAY_ID_NONE;
-        if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
-            // This InputDevice is associated with a viewport.
-            // Only generate events for the associated display.
-            const bool mismatchedPointerDisplay =
-                    isPointer && (viewport->displayId != mPointerController->getDisplayId());
-            mDisplayId = mismatchedPointerDisplay ? std::nullopt
-                                                  : std::make_optional(viewport->displayId);
-        } else if (isPointer) {
-            // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
-            mDisplayId = mPointerController->getDisplayId();
-        }
-
-        mOrientation = ui::ROTATION_0;
-        const bool isOrientedDevice =
-                (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-        // InputReader works in the un-rotated display coordinate space, so we don't need to do
-        // anything if the device is already orientation-aware. If the device is not
-        // orientation-aware, then we need to apply the inverse rotation of the display so that
-        // when the display rotation is applied later as a part of the per-window transform, we
-        // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
-        // rotations and report values directly from the input device.
-        if (!isOrientedDevice && mDisplayId &&
-            mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
-            if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
-                mOrientation = getInverseRotation(viewport->orientation);
-            }
-        }
-
-        bumpGeneration();
+    if (changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
+        configureOnChangeDisplayInfo(readerConfig);
     }
     return out;
 }
@@ -511,4 +424,117 @@
     return mDisplayId;
 }
 
+void CursorInputMapper::configureWithZeroChanges(const InputReaderConfiguration& readerConfig) {
+    // Configuration with zero changes
+    configureBasicParams();
+    if (mParameters.mode != Parameters::Mode::NAVIGATION &&
+        readerConfig.pointerCaptureRequest.enable) {
+        configureOnPointerCapture(readerConfig);
+    }
+    configureOnChangePointerSpeed(readerConfig);
+    configureOnChangeDisplayInfo(readerConfig);
+}
+
+void CursorInputMapper::configureBasicParams() {
+    mCursorScrollAccumulator.configure(getDeviceContext());
+
+    // Configure basic parameters.
+    configureParameters();
+
+    // Configure device mode.
+    switch (mParameters.mode) {
+        case Parameters::Mode::POINTER_RELATIVE:
+            // Should not happen during first time configuration.
+            ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
+            mParameters.mode = Parameters::Mode::POINTER;
+            [[fallthrough]];
+        case Parameters::Mode::POINTER:
+            mSource = AINPUT_SOURCE_MOUSE;
+            mXPrecision = 1.0f;
+            mYPrecision = 1.0f;
+            mXScale = 1.0f;
+            mYScale = 1.0f;
+            mPointerController = getContext()->getPointerController(getDeviceId());
+            break;
+        case Parameters::Mode::NAVIGATION:
+            mSource = AINPUT_SOURCE_TRACKBALL;
+            mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+            mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+            mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+            mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+            break;
+    }
+
+    mVWheelScale = 1.0f;
+    mHWheelScale = 1.0f;
+}
+
+void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration& config) {
+    if (config.pointerCaptureRequest.enable) {
+        if (mParameters.mode == Parameters::Mode::POINTER) {
+            mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
+            mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
+            // Keep PointerController around in order to preserve the pointer position.
+            mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+        } else {
+            ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
+        }
+    } else {
+        if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+            mParameters.mode = Parameters::Mode::POINTER;
+            mSource = AINPUT_SOURCE_MOUSE;
+        } else {
+            ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
+        }
+    }
+    bumpGeneration();
+}
+
+void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfiguration& config) {
+    if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+        // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
+        mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
+        mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
+        mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
+    } else {
+        mPointerVelocityControl.setParameters(config.pointerVelocityControlParameters);
+        mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
+        mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
+    }
+}
+
+void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
+    const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+    mDisplayId = ADISPLAY_ID_NONE;
+    if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+        // This InputDevice is associated with a viewport.
+        // Only generate events for the associated display.
+        const bool mismatchedPointerDisplay =
+                isPointer && (viewport->displayId != mPointerController->getDisplayId());
+        mDisplayId =
+                mismatchedPointerDisplay ? std::nullopt : std::make_optional(viewport->displayId);
+    } else if (isPointer) {
+        // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+        mDisplayId = mPointerController->getDisplayId();
+    }
+
+    mOrientation = ui::ROTATION_0;
+    const bool isOrientedDevice =
+            (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
+    // InputReader works in the un-rotated display coordinate space, so we don't need to do
+    // anything if the device is already orientation-aware. If the device is not
+    // orientation-aware, then we need to apply the inverse rotation of the display so that
+    // when the display rotation is applied later as a part of the per-window transform, we
+    // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
+    // rotations and report values directly from the input device.
+    if (!isOrientedDevice && mDisplayId && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+        if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
+            mOrientation = getInverseRotation(viewport->orientation);
+        }
+    }
+
+    bumpGeneration();
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 5f7a3ad..caf2e5a 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -53,15 +53,16 @@
 
 class CursorInputMapper : public InputMapper {
 public:
-    explicit CursorInputMapper(InputDeviceContext& deviceContext);
+    explicit CursorInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
     virtual ~CursorInputMapper();
 
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
-                                                    const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    const InputReaderConfiguration& readerConfig,
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
@@ -126,6 +127,11 @@
 
     void configureParameters();
     void dumpParameters(std::string& dump);
+    void configureWithZeroChanges(const InputReaderConfiguration& readerConfig);
+    void configureBasicParams();
+    void configureOnPointerCapture(const InputReaderConfiguration& config);
+    void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
+    void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
 
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index bbb641e..987d2d0 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -23,8 +23,9 @@
 
 namespace android {
 
-ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {}
+ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext,
+                                                     const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig), mTouchButtonAccumulator(deviceContext) {}
 
 uint32_t ExternalStylusInputMapper::getSources() const {
     return AINPUT_SOURCE_STYLUS;
@@ -48,7 +49,7 @@
 
 std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when,
                                                              const InputReaderConfiguration& config,
-                                                             uint32_t changes) {
+                                                             ConfigurationChanges changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
     mTouchButtonAccumulator.configure();
     return {};
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 3eac10d..841c437 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -26,7 +26,8 @@
 
 class ExternalStylusInputMapper : public InputMapper {
 public:
-    explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
+    explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext,
+                                       const InputReaderConfiguration& readerConfig);
     virtual ~ExternalStylusInputMapper() = default;
 
     uint32_t getSources() const override;
@@ -34,7 +35,7 @@
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 5dd7bc4..0692dbb 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -25,7 +25,9 @@
 
 namespace android {
 
-InputMapper::InputMapper(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+InputMapper::InputMapper(InputDeviceContext& deviceContext,
+                         const InputReaderConfiguration& readerConfig)
+      : mDeviceContext(deviceContext) {}
 
 InputMapper::~InputMapper() {}
 
@@ -36,7 +38,7 @@
 void InputMapper::dump(std::string& dump) {}
 
 std::list<NotifyArgs> InputMapper::reconfigure(nsecs_t when, const InputReaderConfiguration& config,
-                                               uint32_t changes) {
+                                               ConfigurationChanges changes) {
     return {};
 }
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index ab573f0..f017317 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -31,16 +31,16 @@
  * different classes of events.
  *
  * InputMapper lifecycle:
- * - create
- * - configure with 0 changes
+ * - create and configure with 0 changes
  * - reset
- * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
+ * - process, process, process (may occasionally reconfigure or reset)
  * - reset
  * - destroy
  */
 class InputMapper {
 public:
-    explicit InputMapper(InputDeviceContext& deviceContext);
+    explicit InputMapper(InputDeviceContext& deviceContext,
+                         const InputReaderConfiguration& readerConfig);
     virtual ~InputMapper();
 
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
@@ -55,7 +55,7 @@
     virtual void dump(std::string& dump);
     [[nodiscard]] virtual std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                             const InputReaderConfiguration& config,
-                                                            uint32_t changes);
+                                                            ConfigurationChanges changes);
     [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
     [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
     [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 3e840ee..099a955 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -20,8 +20,9 @@
 
 namespace android {
 
-JoystickInputMapper::JoystickInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
+JoystickInputMapper::JoystickInputMapper(InputDeviceContext& deviceContext,
+                                         const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig) {}
 
 JoystickInputMapper::~JoystickInputMapper() {}
 
@@ -105,10 +106,10 @@
 
 std::list<NotifyArgs> JoystickInputMapper::reconfigure(nsecs_t when,
                                                        const InputReaderConfiguration& config,
-                                                       uint32_t changes) {
+                                                       ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
 
-    if (!changes) { // first time only
+    if (!changes.any()) { // first time only
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
             if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 6f1c6b7..49673a2 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -22,7 +22,8 @@
 
 class JoystickInputMapper : public InputMapper {
 public:
-    explicit JoystickInputMapper(InputDeviceContext& deviceContext);
+    explicit JoystickInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
     virtual ~JoystickInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -30,7 +31,7 @@
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f8dd3a8..7388752 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -63,9 +63,10 @@
 
 // --- KeyboardInputMapper ---
 
-KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source,
-                                         int32_t keyboardType)
-      : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}
+KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
+                                         const InputReaderConfiguration& readerConfig,
+                                         uint32_t source, int32_t keyboardType)
+      : InputMapper(deviceContext, readerConfig), mSource(source), mKeyboardType(keyboardType) {}
 
 uint32_t KeyboardInputMapper::getSources() const {
     return mSource;
@@ -133,21 +134,26 @@
 
 std::list<NotifyArgs> KeyboardInputMapper::reconfigure(nsecs_t when,
                                                        const InputReaderConfiguration& config,
-                                                       uint32_t changes) {
+                                                       ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
 
-    if (!changes) { // first time only
+    if (!changes.any()) { // first time only
         // Configure basic parameters.
         configureParameters();
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
         mViewport = findViewport(config);
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION)) {
-        mKeyboardLayoutInfo =
+    if (!changes.any() ||
+        changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION)) {
+        std::optional<KeyboardLayoutInfo> newKeyboardLayoutInfo =
                 getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
+        if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
+            mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+            bumpGeneration();
+        }
     }
 
     return out;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0cb130d..bd27383 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -23,7 +23,9 @@
 
 class KeyboardInputMapper : public InputMapper {
 public:
-    KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
+    KeyboardInputMapper(InputDeviceContext& deviceContext,
+                        const InputReaderConfiguration& readerConfig, uint32_t source,
+                        int32_t keyboardType);
     ~KeyboardInputMapper() override = default;
 
     uint32_t getSources() const override;
@@ -31,7 +33,7 @@
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index e871288..9c87c62 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -28,8 +28,9 @@
 
 // --- MultiTouchInputMapper ---
 
-MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
-      : TouchInputMapper(deviceContext) {}
+MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext,
+                                             const InputReaderConfiguration& readerConfig)
+      : TouchInputMapper(deviceContext, readerConfig) {}
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {}
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 5f8bccf..a617420 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -23,7 +23,8 @@
 
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
-    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
+    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+                                   const InputReaderConfiguration& readerConfig);
     ~MultiTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index b181aa0..13f2e59 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -26,8 +26,9 @@
 
 namespace android {
 
-RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) {
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+                                                   const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) {
     mSource = AINPUT_SOURCE_ROTARY_ENCODER;
 }
 
@@ -65,12 +66,12 @@
 
 std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
                                                             const InputReaderConfiguration& config,
-                                                            uint32_t changes) {
+                                                            ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
-    if (!changes) {
+    if (!changes.any()) {
         mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
     }
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
                 config.getDisplayViewportByType(ViewportType::INTERNAL);
         if (internalViewport) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 37c9442..d3dcbe1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -25,7 +25,8 @@
 
 class RotaryEncoderInputMapper : public InputMapper {
 public:
-    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext);
+    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+                                      const InputReaderConfiguration& readerConfig);
     virtual ~RotaryEncoderInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -33,7 +34,7 @@
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index f8a520d..a131e35 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -52,8 +52,9 @@
     }
 }
 
-SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
+SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext,
+                                     const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig) {}
 
 SensorInputMapper::~SensorInputMapper() {}
 
@@ -118,10 +119,10 @@
 
 std::list<NotifyArgs> SensorInputMapper::reconfigure(nsecs_t when,
                                                      const InputReaderConfiguration& config,
-                                                     uint32_t changes) {
+                                                     ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
 
-    if (!changes) { // first time only
+    if (!changes.any()) { // first time only
         mDeviceEnabled = true;
         // Check if device has MSC_TIMESTAMP event.
         mHasHardwareTimestamp = getDeviceContext().hasMscEvent(MSC_TIMESTAMP);
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index fa36ab3..1f82559 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -27,7 +27,8 @@
 
 class SensorInputMapper : public InputMapper {
 public:
-    explicit SensorInputMapper(InputDeviceContext& deviceContext);
+    explicit SensorInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
     ~SensorInputMapper() override;
 
     uint32_t getSources() const override;
@@ -35,7 +36,7 @@
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index f13417a..ed0e270 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -18,8 +18,9 @@
 
 namespace android {
 
-SingleTouchInputMapper::SingleTouchInputMapper(InputDeviceContext& deviceContext)
-      : TouchInputMapper(deviceContext) {}
+SingleTouchInputMapper::SingleTouchInputMapper(InputDeviceContext& deviceContext,
+                                               const InputReaderConfiguration& readerConfig)
+      : TouchInputMapper(deviceContext, readerConfig) {}
 
 SingleTouchInputMapper::~SingleTouchInputMapper() {}
 
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 662e6bc..9341007 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -23,7 +23,8 @@
 
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
-    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
+    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+                                    const InputReaderConfiguration& readerConfig);
     ~SingleTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index c4564a4..05338da 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -20,8 +20,9 @@
 
 namespace android {
 
-SwitchInputMapper::SwitchInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mSwitchValues(0), mUpdatedSwitchMask(0) {}
+SwitchInputMapper::SwitchInputMapper(InputDeviceContext& deviceContext,
+                                     const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig), mSwitchValues(0), mUpdatedSwitchMask(0) {}
 
 SwitchInputMapper::~SwitchInputMapper() {}
 
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 06d6504..7ec282b 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -22,7 +22,8 @@
 
 class SwitchInputMapper : public InputMapper {
 public:
-    explicit SwitchInputMapper(InputDeviceContext& deviceContext);
+    explicit SwitchInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources() const override;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 9a426bf..c72a263 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -121,12 +121,12 @@
 
 // --- TouchInputMapper ---
 
-TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext),
+TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext,
+                                   const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig),
         mTouchButtonAccumulator(deviceContext),
-        mSource(0),
-        mDeviceMode(DeviceMode::DISABLED),
-        mInputDeviceOrientation(ui::ROTATION_0) {}
+        mConfig(readerConfig),
+        mParameters(computeParameters(deviceContext)) {}
 
 TouchInputMapper::~TouchInputMapper() {}
 
@@ -289,7 +289,7 @@
 
 std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) {
+                                                    ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
 
     mConfig = config;
@@ -297,9 +297,9 @@
     // Full configuration should happen the first time configure is called and
     // when the device type is changed. Changing a device type can affect
     // various other parameters so should result in a reconfiguration.
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DEVICE_TYPE)) {
         // Configure basic parameters.
-        configureParameters();
+        mParameters = computeParameters(getDeviceContext());
 
         // Configure common accumulators.
         mCursorScrollAccumulator.configure(getDeviceContext());
@@ -313,33 +313,34 @@
         resolveCalibration();
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
+    if (!changes.any() ||
+        changes.test(InputReaderConfiguration::Change::TOUCH_AFFINE_TRANSFORMATION)) {
         // Update location calibration to reflect current settings
         updateAffineTransformation();
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED)) {
         // Update pointer speed.
         mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
         mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
         mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
     }
 
+    using namespace ftl::flag_operators;
     bool resetNeeded = false;
-    if (!changes ||
-        (changes &
-         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-          InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
-          InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
-          InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
-          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE |
-          InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
+    if (!changes.any() ||
+        changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
+                    InputReaderConfiguration::Change::POINTER_CAPTURE |
+                    InputReaderConfiguration::Change::POINTER_GESTURE_ENABLEMENT |
+                    InputReaderConfiguration::Change::SHOW_TOUCHES |
+                    InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE |
+                    InputReaderConfiguration::Change::DEVICE_TYPE)) {
         // Configure device sources, display dimensions, orientation and
         // scaling factors.
         configureInputDevice(when, &resetNeeded);
     }
 
-    if (changes && resetNeeded) {
+    if (changes.any() && resetNeeded) {
         out += reset(when);
 
         // Send reset, unless this is the first time the device has been configured,
@@ -359,112 +360,119 @@
     }
 }
 
-void TouchInputMapper::configureParameters() {
+TouchInputMapper::Parameters TouchInputMapper::computeParameters(
+        const InputDeviceContext& deviceContext) {
+    Parameters parameters;
     // Use the pointer presentation mode for devices that do not support distinct
     // multitouch.  The spot-based presentation relies on being able to accurately
     // locate two or more fingers on the touch pad.
-    mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
+    parameters.gestureMode = deviceContext.hasInputProperty(INPUT_PROP_SEMI_MT)
             ? Parameters::GestureMode::SINGLE_TOUCH
             : Parameters::GestureMode::MULTI_TOUCH;
 
-    const PropertyMap& config = getDeviceContext().getConfiguration();
+    const PropertyMap& config = deviceContext.getConfiguration();
     std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
     if (gestureModeString.has_value()) {
         if (*gestureModeString == "single-touch") {
-            mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
+            parameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
         } else if (*gestureModeString == "multi-touch") {
-            mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
+            parameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
         } else if (*gestureModeString != "default") {
             ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
         }
     }
 
-    configureDeviceType();
+    parameters.deviceType = computeDeviceType(deviceContext);
 
-    mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
+    parameters.hasButtonUnderPad = deviceContext.hasInputProperty(INPUT_PROP_BUTTONPAD);
 
-    mParameters.orientationAware =
+    parameters.orientationAware =
             config.getBool("touch.orientationAware")
-                    .value_or(mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
+                    .value_or(parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
 
-    mParameters.orientation = ui::ROTATION_0;
+    parameters.orientation = ui::ROTATION_0;
     std::optional<std::string> orientationString = config.getString("touch.orientation");
     if (orientationString.has_value()) {
-        if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+        if (parameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
             ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
         } else if (*orientationString == "ORIENTATION_90") {
-            mParameters.orientation = ui::ROTATION_90;
+            parameters.orientation = ui::ROTATION_90;
         } else if (*orientationString == "ORIENTATION_180") {
-            mParameters.orientation = ui::ROTATION_180;
+            parameters.orientation = ui::ROTATION_180;
         } else if (*orientationString == "ORIENTATION_270") {
-            mParameters.orientation = ui::ROTATION_270;
+            parameters.orientation = ui::ROTATION_270;
         } else if (*orientationString != "ORIENTATION_0") {
             ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
         }
     }
 
-    mParameters.hasAssociatedDisplay = false;
-    mParameters.associatedDisplayIsExternal = false;
-    if (mParameters.orientationAware ||
-        mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
-        mParameters.deviceType == Parameters::DeviceType::POINTER ||
-        (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
-         getDeviceContext().getAssociatedViewport())) {
-        mParameters.hasAssociatedDisplay = true;
-        if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
-            mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
-            mParameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
+    parameters.hasAssociatedDisplay = false;
+    parameters.associatedDisplayIsExternal = false;
+    if (parameters.orientationAware ||
+        parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
+        parameters.deviceType == Parameters::DeviceType::POINTER ||
+        (parameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
+         deviceContext.getAssociatedViewport())) {
+        parameters.hasAssociatedDisplay = true;
+        if (parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
+            parameters.associatedDisplayIsExternal = deviceContext.isExternal();
+            parameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
         }
     }
-    if (getDeviceContext().getAssociatedDisplayPort()) {
-        mParameters.hasAssociatedDisplay = true;
+    if (deviceContext.getAssociatedDisplayPort()) {
+        parameters.hasAssociatedDisplay = true;
     }
 
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
     // up in your pocket but you can enable it using the input device configuration.
-    mParameters.wake = config.getBool("touch.wake").value_or(getDeviceContext().isExternal());
+    parameters.wake = config.getBool("touch.wake").value_or(deviceContext.isExternal());
 
     std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
     std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
     if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
-        mParameters.usiVersion = {
+        parameters.usiVersion = {
                 .majorVersion = *usiVersionMajor,
                 .minorVersion = *usiVersionMinor,
         };
     }
 
-    mParameters.enableForInactiveViewport =
+    parameters.enableForInactiveViewport =
             config.getBool("touch.enableForInactiveViewport").value_or(false);
+
+    return parameters;
 }
 
-void TouchInputMapper::configureDeviceType() {
-    if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
+TouchInputMapper::Parameters::DeviceType TouchInputMapper::computeDeviceType(
+        const InputDeviceContext& deviceContext) {
+    Parameters::DeviceType deviceType;
+    if (deviceContext.hasInputProperty(INPUT_PROP_DIRECT)) {
         // The device is a touch screen.
-        mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
-    } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
+        deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+    } else if (deviceContext.hasInputProperty(INPUT_PROP_POINTER)) {
         // The device is a pointing device like a track pad.
-        mParameters.deviceType = Parameters::DeviceType::POINTER;
+        deviceType = Parameters::DeviceType::POINTER;
     } else {
         // The device is a touch pad of unknown purpose.
-        mParameters.deviceType = Parameters::DeviceType::POINTER;
+        deviceType = Parameters::DeviceType::POINTER;
     }
 
     // Type association takes precedence over the device type found in the idc file.
-    std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
+    std::string deviceTypeString = deviceContext.getDeviceTypeAssociation().value_or("");
     if (deviceTypeString.empty()) {
         deviceTypeString =
-                getDeviceContext().getConfiguration().getString("touch.deviceType").value_or("");
+                deviceContext.getConfiguration().getString("touch.deviceType").value_or("");
     }
     if (deviceTypeString == "touchScreen") {
-        mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+        deviceType = Parameters::DeviceType::TOUCH_SCREEN;
     } else if (deviceTypeString == "touchNavigation") {
-        mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
+        deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
     } else if (deviceTypeString == "pointer") {
-        mParameters.deviceType = Parameters::DeviceType::POINTER;
+        deviceType = Parameters::DeviceType::POINTER;
     } else if (deviceTypeString != "default" && deviceTypeString != "") {
         ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
     }
+    return deviceType;
 }
 
 void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -3600,17 +3608,19 @@
                         "%s cannot be used when the device is not in POINTER mode.", __func__);
     std::list<NotifyArgs> out;
     int32_t metaState = getContext()->getGlobalMetaState();
+    auto cursorPosition = mPointerSimple.currentCoords.getXYValue();
 
-    if (down || hovering) {
-        mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
-        mPointerController->clearSpots();
-        mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
-    } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
-        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+    if (displayId == mPointerController->getDisplayId()) {
+        std::tie(cursorPosition.x, cursorPosition.y) = mPointerController->getPosition();
+        if (down || hovering) {
+            mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+            mPointerController->clearSpots();
+            mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+        } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
+            mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+        }
     }
 
-    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
-
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
 
@@ -3620,8 +3630,9 @@
                                        0, metaState, mLastRawState.buttonState,
                                        MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                        &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                       yCursorPosition, mPointerSimple.downTime,
+                                       mOrientedXPrecision, mOrientedYPrecision,
+                                       mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
+                                       mPointerSimple.downTime,
                                        /* videoFrames */ {}));
     }
 
@@ -3629,15 +3640,15 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                       mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                                       mLastRawState.buttonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                       &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                       yCursorPosition, mPointerSimple.downTime,
-                                       /* videoFrames */ {}));
+        out.push_back(
+                NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                 displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+                                 metaState, mLastRawState.buttonState, MotionClassification::NONE,
+                                 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                                 &mPointerSimple.lastCoords, mOrientedXPrecision,
+                                 mOrientedYPrecision, mPointerSimple.lastCursorX,
+                                 mPointerSimple.lastCursorY, mPointerSimple.downTime,
+                                 /* videoFrames */ {}));
     }
 
     if (down) {
@@ -3653,7 +3664,7 @@
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                            &mPointerSimple.currentProperties,
                                            &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
                                            mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
@@ -3664,7 +3675,7 @@
                                        MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                        &mPointerSimple.currentProperties,
                                        &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                       mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
                                        mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
@@ -3680,7 +3691,7 @@
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                            &mPointerSimple.currentProperties,
                                            &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
                                            mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
@@ -3691,8 +3702,8 @@
                                  metaState, mCurrentRawState.buttonState,
                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                 mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                 yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}));
+                                 mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
+                                 cursorPosition.y, mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3712,8 +3723,8 @@
                                        0, 0, metaState, mCurrentRawState.buttonState,
                                        MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                        &mPointerSimple.currentProperties, &pointerCoords,
-                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                       yCursorPosition, mPointerSimple.downTime,
+                                       mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
+                                       cursorPosition.y, mPointerSimple.downTime,
                                        /* videoFrames */ {}));
     }
 
@@ -3723,8 +3734,8 @@
         mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
         mPointerSimple.displayId = displayId;
         mPointerSimple.source = mSource;
-        mPointerSimple.lastCursorX = xCursorPosition;
-        mPointerSimple.lastCursorY = yCursorPosition;
+        mPointerSimple.lastCursorX = cursorPosition.x;
+        mPointerSimple.lastCursorY = cursorPosition.y;
     } else {
         mPointerSimple.reset();
     }
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 0e8ff4b..7141924 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -146,7 +146,8 @@
 
 class TouchInputMapper : public InputMapper {
 public:
-    explicit TouchInputMapper(InputDeviceContext& deviceContext);
+    explicit TouchInputMapper(InputDeviceContext& deviceContext,
+                              const InputReaderConfiguration& readerConfig);
     ~TouchInputMapper() override;
 
     uint32_t getSources() const override;
@@ -154,7 +155,7 @@
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
@@ -191,7 +192,7 @@
     };
 
     // Input sources and device mode.
-    uint32_t mSource;
+    uint32_t mSource{0};
 
     enum class DeviceMode {
         DISABLED,   // input is disabled
@@ -202,7 +203,7 @@
 
         ftl_last = POINTER
     };
-    DeviceMode mDeviceMode;
+    DeviceMode mDeviceMode{DeviceMode::DISABLED};
 
     // The reader's configuration.
     InputReaderConfiguration mConfig;
@@ -376,7 +377,6 @@
 
     std::vector<VirtualKey> mVirtualKeys;
 
-    virtual void configureParameters();
     virtual void dumpParameters(std::string& dump);
     virtual void configureRawPointerAxes();
     virtual void dumpRawPointerAxes(std::string& dump);
@@ -412,7 +412,7 @@
     // The orientation of the input device relative to that of the display panel. It specifies
     // the rotation of the input device coordinates required to produce the display panel
     // orientation, so it will depend on whether the device is orientation aware.
-    ui::Rotation mInputDeviceOrientation;
+    ui::Rotation mInputDeviceOrientation{ui::ROTATION_0};
 
     // The transform that maps the input device's raw coordinate space to the un-rotated display's
     // coordinate space. InputReader generates events in the un-rotated display's coordinate space.
@@ -823,8 +823,8 @@
 
     // Compute input transforms for DIRECT and POINTER modes.
     void computeInputTransforms();
-
-    void configureDeviceType();
+    static Parameters::DeviceType computeDeviceType(const InputDeviceContext& deviceContext);
+    static Parameters computeParameters(const InputDeviceContext& deviceContext);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 6b1d409..8753b48 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -169,8 +169,9 @@
 
 } // namespace
 
-TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext),
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
+                                         const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig),
         mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
         mPointerController(getContext()->getPointerController(getDeviceId())),
         mStateConverter(deviceContext),
@@ -221,13 +222,13 @@
 
 std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
                                                        const InputReaderConfiguration& config,
-                                                       uint32_t changes) {
-    if (!changes) {
+                                                       ConfigurationChanges changes) {
+    if (!changes.any()) {
         // First time configuration
         mPropertyProvider.loadPropertiesFromIdcFile(getDeviceContext().getConfiguration());
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
         std::optional<int32_t> displayId = mPointerController->getDisplayId();
         ui::Rotation orientation = ui::ROTATION_0;
         if (displayId.has_value()) {
@@ -237,7 +238,7 @@
         }
         mGestureConverter.setOrientation(orientation);
     }
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
         mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
                 .setBoolValues({true});
         GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve");
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 27cdde1..268b275 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -37,7 +37,8 @@
 
 class TouchpadInputMapper : public InputMapper {
 public:
-    explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+    explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
     ~TouchpadInputMapper();
 
     uint32_t getSources() const override;
@@ -46,7 +47,7 @@
 
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
-                                                    uint32_t changes) override;
+                                                    ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 2c77fc4..8d78d0f 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -20,8 +20,9 @@
 
 namespace android {
 
-VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mVibrating(false), mSequence(0) {}
+VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext,
+                                         const InputReaderConfiguration& readerConfig)
+      : InputMapper(deviceContext, readerConfig), mVibrating(false), mSequence(0) {}
 
 VibratorInputMapper::~VibratorInputMapper() {}
 
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index e665973..384c075 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -22,7 +22,8 @@
 
 class VibratorInputMapper : public InputMapper {
 public:
-    explicit VibratorInputMapper(InputDeviceContext& deviceContext);
+    explicit VibratorInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
     virtual ~VibratorInputMapper();
 
     virtual uint32_t getSources() const override;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5e51bfc..27d7b9c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -818,8 +818,7 @@
 
 TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
     constexpr nsecs_t eventTime = 20;
-    NotifyConfigurationChangedArgs args(/*id=*/10, eventTime);
-    mDispatcher->notifyConfigurationChanged(&args);
+    mDispatcher->notifyConfigurationChanged({/*id=*/10, eventTime});
     ASSERT_TRUE(mDispatcher->waitForIdle());
 
     mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
@@ -828,7 +827,7 @@
 TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
     NotifySwitchArgs args(/*id=*/10, /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1,
                           /*switchMask=*/2);
-    mDispatcher->notifySwitch(&args);
+    mDispatcher->notifySwitch(args);
 
     // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
     args.policyFlags |= POLICY_FLAG_TRUSTED;
@@ -1743,8 +1742,9 @@
     return args;
 }
 
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
-                                           const std::vector<PointF>& points) {
+[[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
+                                                         int32_t displayId,
+                                                         const std::vector<PointF>& points) {
     size_t pointerCount = points.size();
     if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
         EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
@@ -1946,26 +1946,20 @@
             sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-    NotifyMotionArgs args;
     // First touch pointer down on right window
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .build());
     // Second touch pointer down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(100))
+                                      .build());
     // First touch pointer lifts. The second one remains down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
-
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(100))
+                                      .build());
     window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
@@ -2256,54 +2250,48 @@
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
     const int32_t touchDeviceId = 4;
-    NotifyMotionArgs args;
 
     // Two pointers down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .policyFlags(DEFAULT_POLICY_FLAGS)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .deviceId(touchDeviceId)
+                    .policyFlags(DEFAULT_POLICY_FLAGS)
+                    .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                    .build());
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .policyFlags(DEFAULT_POLICY_FLAGS)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
     spyWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
 
     // Cancel the current gesture. Send the cancel without the default policy flags.
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .policyFlags(0)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
+                    .deviceId(touchDeviceId)
+                    .policyFlags(0)
+                    .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                    .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+                    .build());
     spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL));
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL));
 
     // We don't need to reset the device to reproduce the issue, but the reset event typically
     // follows, so we keep it here to model the actual listener behaviour more closely.
-    NotifyDeviceResetArgs resetArgs;
-    resetArgs.id = 1; // arbitrary id
-    resetArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    resetArgs.deviceId = touchDeviceId;
-    mDispatcher->notifyDeviceReset(&resetArgs);
+    mDispatcher->notifyDeviceReset({/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), touchDeviceId});
 
     // Start new gesture
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .policyFlags(DEFAULT_POLICY_FLAGS)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .deviceId(touchDeviceId)
+                    .policyFlags(DEFAULT_POLICY_FLAGS)
+                    .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                    .build());
     spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
 
@@ -2453,53 +2441,49 @@
     NotifyMotionArgs args;
 
     // Start hovering over the left window
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                                      .build());
     leftWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
 
     // Mouse down on left window
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                                      .build());
 
     leftWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
     leftWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+                    .deviceId(mouseDeviceId)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                    .build());
     leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     // First touch pointer down on right window
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .build());
     leftWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
 
     rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
 
     // Second touch pointer down on left window
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
+                                      .build());
     leftWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
     // This MOVE event is not necessary (doesn't carry any new information), but it's there in the
@@ -2533,57 +2517,52 @@
     NotifyMotionArgs args;
 
     // First touch pointer down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .build());
     // Second touch pointer down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+                                      .build());
     // First touch pointer lifts. The second one remains down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+                                      .build());
     window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
 
     // Mouse down. The touch should be canceled
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
+                                      .build());
 
     window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
                                      WithPointerCount(1u)));
     window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+                    .deviceId(mouseDeviceId)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
+                    .build());
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     // Second touch pointer down.
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_0_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+                                      .build());
     // The pointer_down event should be ignored
     window->assertNoEvents();
 }
@@ -2617,11 +2596,10 @@
 
     // Now a real touch comes. Rather than crashing or dropping the real event, the injected pointer
     // should be canceled and the new gesture should take over.
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+                                      .build());
 
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
@@ -2823,46 +2801,38 @@
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
 
-    NotifyMotionArgs args;
-
     // Start hovering with stylus
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
     // Stop hovering
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
 
     // Stylus touches down
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
 
     // Stylus goes up
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
 
     // Again hover
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
     // Stop hovering
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
-                             .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
 
     // No more events
@@ -2891,35 +2861,31 @@
 
     const int32_t mouseDeviceId = 7;
     const int32_t touchDeviceId = 4;
-    NotifyMotionArgs args;
 
     // Hover a bit with mouse first
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                                      .build());
     spyWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
 
     // Start touching
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                             .deviceId(touchDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
     window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
 
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
-                             .deviceId(touchDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::FINGER).x(55).y(55))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(55).y(55))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
     window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
 
@@ -2927,20 +2893,18 @@
     EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindow->getToken()));
     window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
 
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
-                             .deviceId(touchDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
 
     // Mouse down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                                      .build());
 
     spyWindow->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
@@ -2948,32 +2912,30 @@
             AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
     window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+                    .deviceId(mouseDeviceId)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+                    .build());
     spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     // Mouse move!
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+                                      .build());
     spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
     window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
 
     // Touch move!
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
-                             .deviceId(touchDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::FINGER).x(65).y(65))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(65).y(65))
+                                      .build());
 
     // No more events
     spyWindow->assertNoEvents();
@@ -2991,16 +2953,15 @@
             sp<FakeWindowHandle>::make(application, mDispatcher, "Window", DISPLAY_ID);
 
     mDispatcher->setInputWindows({{DISPLAY_ID, {window}}});
-    NotifyMotionArgs args;
 
     // Touch down on the empty space
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}})));
+    mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}}));
 
     mDispatcher->waitForIdle();
     window->assertNoEvents();
 
     // Now touch down on the window with another pointer
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{-1, -1}, {10, 10}})));
+    mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{-1, -1}, {10, 10}}));
     mDispatcher->waitForIdle();
     window->consumeMotionDown();
 }
@@ -3021,16 +2982,15 @@
 
     mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
 
-    NotifyMotionArgs args;
     // Touch down on the non-touchable window
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})));
+    mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
 
     mDispatcher->waitForIdle();
     window1->assertNoEvents();
     window2->assertNoEvents();
 
     // Now touch down on the window with another pointer
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
+    mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
     mDispatcher->waitForIdle();
     window2->consumeMotionDown();
 }
@@ -3050,9 +3010,8 @@
 
     mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
 
-    NotifyMotionArgs args;
     // Touch down on the first window
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})));
+    mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
 
     mDispatcher->waitForIdle();
     InputEvent* inputEvent1 = window1->consume();
@@ -3063,7 +3022,7 @@
     ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
 
     // Now touch down on the window with another pointer
-    mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
+    mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
     mDispatcher->waitForIdle();
     InputEvent* inputEvent2 = window2->consume();
     ASSERT_NE(inputEvent2, nullptr);
@@ -3073,14 +3032,12 @@
     ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
 
     // Now move the pointer on the second window
-    mDispatcher->notifyMotion(
-            &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
+    mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
     mDispatcher->waitForIdle();
     window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
 
     // Now add new touch down on the second window
-    mDispatcher->notifyMotion(
-            &(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
+    mDispatcher->notifyMotion(generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}}));
     mDispatcher->waitForIdle();
     window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
 
@@ -3089,13 +3046,13 @@
     window1->assertNoEvents();
 
     // Now move the pointer on the first window
-    mDispatcher->notifyMotion(&(
-            args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}})));
+    mDispatcher->notifyMotion(
+            generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}}));
     mDispatcher->waitForIdle();
     window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
 
-    mDispatcher->notifyMotion(&(
-            args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
+    mDispatcher->notifyMotion(
+            generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}}));
     mDispatcher->waitForIdle();
     window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
 }
@@ -3199,52 +3156,47 @@
 
     const int32_t touchDeviceId = 4;
     const int32_t mouseDeviceId = 6;
-    NotifyMotionArgs args;
 
     // Two pointers down
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .build());
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+                                      .build());
     window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
     window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
 
     // Inject a series of mouse events for a mouse click
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+                                      .build());
     window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
                                      WithPointerCount(2u)));
     window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
 
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
-                           .deviceId(mouseDeviceId)
-                           .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
-                           .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
-                           .build()));
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+                    .deviceId(mouseDeviceId)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+                    .build());
     window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     // Try to send more touch events while the mouse is down. Since it's a continuation of an
     // already canceled gesture, it should be ignored.
-    mDispatcher->notifyMotion(&(
-            args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
-                           .deviceId(touchDeviceId)
-                           .pointer(PointerBuilder(0, ToolType::FINGER).x(101).y(101))
-                           .pointer(PointerBuilder(1, ToolType::FINGER).x(121).y(121))
-                           .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(101).y(101))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(121).y(121))
+                                      .build());
     window->assertNoEvents();
 }
 
@@ -3508,23 +3460,20 @@
 
     const int32_t mouseDeviceId = 7;
     const int32_t touchDeviceId = 4;
-    NotifyMotionArgs args;
 
     // Start hovering with the mouse
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
-                             .deviceId(mouseDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::MOUSE).x(10).y(10))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(10).y(10))
+                                      .build());
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
 
     // Touch goes down
-    mDispatcher->notifyMotion(
-            &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                             .deviceId(touchDeviceId)
-                             .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
-                             .build()));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .build());
 
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
@@ -3550,15 +3499,15 @@
                                ADISPLAY_ID_DEFAULT, {{50, 50}});
     motionArgs.xCursorPosition = 50;
     motionArgs.yCursorPosition = 50;
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(motionArgs);
     ASSERT_NO_FATAL_FAILURE(
             window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                                              WithSource(AINPUT_SOURCE_MOUSE))));
 
     // Tap on the window
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {{10, 10}}));
     ASSERT_NO_FATAL_FAILURE(
             window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
                                              WithSource(AINPUT_SOURCE_MOUSE))));
@@ -3567,9 +3516,8 @@
             window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                                              WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT, {{10, 10}}));
     ASSERT_NO_FATAL_FAILURE(
             window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
                                              WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
@@ -3661,16 +3609,14 @@
 
     window->consumeFocusEvent(true);
 
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
 
     // Window should receive key down event.
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 
     // When device reset happens, that key stream should be terminated with FLAG_CANCELED
     // on the app side.
-    NotifyDeviceResetArgs args(/*id=*/10, /*eventTime=*/20, DEVICE_ID);
-    mDispatcher->notifyDeviceReset(&args);
+    mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
     window->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
                          AKEY_EVENT_FLAG_CANCELED);
 }
@@ -3682,18 +3628,15 @@
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
 
     // Window should receive motion down event.
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 
     // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
     // on the app side.
-    NotifyDeviceResetArgs args(/*id=*/10, /*eventTime=*/20, DEVICE_ID);
-    mDispatcher->notifyDeviceReset(&args);
+    mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
 }
@@ -3709,11 +3652,11 @@
 
     window->consumeFocusEvent(true);
 
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
     const std::chrono::milliseconds interceptKeyTimeout = 50ms;
     const nsecs_t injectTime = keyArgs.eventTime;
     mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(keyArgs);
     // The dispatching time should be always greater than or equal to intercept key timeout.
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     ASSERT_TRUE((systemTime(SYSTEM_TIME_MONOTONIC) - injectTime) >=
@@ -3731,11 +3674,9 @@
 
     window->consumeFocusEvent(true);
 
-    NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
     mFakePolicy->setInterceptKeyTimeout(150ms);
-    mDispatcher->notifyKey(&keyDown);
-    mDispatcher->notifyKey(&keyUp);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
 
     // Window should receive key event immediately when same key up.
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -3764,10 +3705,9 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
 
     // Tap on first window.
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {PointF{50, 50}}));
     window->consumeMotionDown();
     // The coordinates of the tap in 'outsideWindow' are relative to its top left corner.
     // Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
@@ -3798,18 +3738,17 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, secondWindow, thirdWindow}}});
 
     // First pointer lands outside all windows. `window` does not get ACTION_OUTSIDE.
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {PointF{-10, -10}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {PointF{-10, -10}}));
     window->assertNoEvents();
     secondWindow->assertNoEvents();
 
     // The second pointer lands inside `secondWindow`, which should receive a DOWN event.
     // Now, `window` should get ACTION_OUTSIDE.
-    motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                                    {PointF{-10, -10}, PointF{105, 105}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {PointF{-10, -10}, PointF{105, 105}}));
     const std::map<int32_t, PointF> expectedPointers{{0, PointF{-10, -10}}, {1, PointF{105, 105}}};
     window->consumeMotionEvent(
             AllOf(WithMotionAction(ACTION_OUTSIDE), WithPointers(expectedPointers)));
@@ -3818,9 +3757,9 @@
 
     // The third pointer lands inside `thirdWindow`, which should receive a DOWN event. There is
     // no ACTION_OUTSIDE sent to `window` because one has already been sent for this gesture.
-    motionArgs = generateMotionArgs(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                                    {PointF{-10, -10}, PointF{105, 105}, PointF{205, 205}});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(
+            generateMotionArgs(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {PointF{-10, -10}, PointF{105, 105}, PointF{205, 205}}));
     window->assertNoEvents();
     secondWindow->consumeMotionMove();
     thirdWindow->consumeMotionDown();
@@ -3837,10 +3776,10 @@
 
     window->consumeFocusEvent(true);
 
-    NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyDown);
-    mDispatcher->notifyKey(&keyUp);
+    const NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    const NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(keyDown);
+    mDispatcher->notifyKey(keyUp);
 
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -3850,8 +3789,8 @@
 
     window->consumeFocusEvent(false);
 
-    mDispatcher->notifyKey(&keyDown);
-    mDispatcher->notifyKey(&keyUp);
+    mDispatcher->notifyKey(keyDown);
+    mDispatcher->notifyKey(keyUp);
     window->assertNoEvents();
 }
 
@@ -3959,10 +3898,9 @@
     // Send down to the first window. The point is represented in the display space. The point is
     // selected so that if the hit test was performed with the point and the bounds being in
     // different coordinate spaces, the event would end up in the incorrect window.
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {PointF{75, 55}}));
 
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
@@ -4013,10 +3951,9 @@
     auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
 
     // Send down to the second window.
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {PointF{150, 220}});
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {PointF{150, 220}}));
 
     firstWindow->assertNoEvents();
     const MotionEvent* event = secondWindow->consumeMotion();
@@ -4071,14 +4008,14 @@
     for (const auto pointInsideWindow : insidePoints) {
         const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
         const PointF pointInDisplaySpace{p.x, p.y};
-        const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                             ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
-        mDispatcher->notifyMotion(&down);
+        mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                     AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                     {pointInDisplaySpace}));
         window->consumeMotionDown();
 
-        const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                                           ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
-        mDispatcher->notifyMotion(&up);
+        mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+                                                     AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                     {pointInDisplaySpace}));
         window->consumeMotionUp();
     }
 
@@ -4088,13 +4025,13 @@
     for (const auto pointOutsideWindow : outsidePoints) {
         const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
         const PointF pointInDisplaySpace{p.x, p.y};
-        const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                             ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
-        mDispatcher->notifyMotion(&down);
+        mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                     AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                     {pointInDisplaySpace}));
 
-        const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                                           ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
-        mDispatcher->notifyMotion(&up);
+        mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+                                                     AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                     {pointInDisplaySpace}));
     }
     window->assertNoEvents();
 }
@@ -4132,10 +4069,8 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
 
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
@@ -4152,10 +4087,8 @@
     wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first  window gets no events and the second gets up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionUp();
@@ -4191,10 +4124,8 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, firstWindow, secondWindow}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     // Only the first window and spy should get the down event
     spyWindow->consumeMotionDown();
     firstWindow->consumeMotionDown();
@@ -4209,10 +4140,8 @@
     secondWindow->consumeMotionDown();
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first  window gets no events and the second+spy get up
     firstWindow->assertNoEvents();
     spyWindow->consumeMotionUp();
@@ -4238,19 +4167,16 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {touchPoint});
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {touchPoint}));
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send pointer down to the first window
-    NotifyMotionArgs pointerDownMotionArgs =
-            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {touchPoint, touchPoint});
-    mDispatcher->notifyMotion(&pointerDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}));
     // Only the first window should get the pointer down event
     firstWindow->consumeMotionPointerDown(1);
     secondWindow->assertNoEvents();
@@ -4265,19 +4191,15 @@
     secondWindow->consumeMotionPointerDown(1);
 
     // Send pointer up to the second window
-    NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {touchPoint, touchPoint});
-    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}));
     // The first window gets nothing and the second gets pointer up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionPointerUp(1);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first window gets nothing and the second gets up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionUp();
@@ -4308,10 +4230,8 @@
             {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&downMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
 
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
@@ -4331,10 +4251,8 @@
     wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first  window gets no events and the second gets up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionUp();
@@ -4378,19 +4296,17 @@
     PointF pointInSecond = {300, 600};
 
     // Send down to the first window
-    NotifyMotionArgs firstDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {pointInFirst});
-    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst}));
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send down to the second window
-    NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {pointInFirst, pointInSecond});
-    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst, pointInSecond}));
     // The first window gets a move and the second a down
     firstWindow->consumeMotionMove();
     secondWindow->consumeMotionDown();
@@ -4402,19 +4318,16 @@
     secondWindow->consumeMotionPointerDown(1);
 
     // Send pointer up to the second window
-    NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {pointInFirst, pointInSecond});
-    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst, pointInSecond}));
     // The first window gets nothing and the second gets pointer up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionPointerUp(1);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first window gets nothing and the second gets up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionUp();
@@ -4444,19 +4357,17 @@
     PointF pointInSecond = {300, 600};
 
     // Send down to the first window
-    NotifyMotionArgs firstDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {pointInFirst});
-    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst}));
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send down to the second window
-    NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {pointInFirst, pointInSecond});
-    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst, pointInSecond}));
     // The first window gets a move and the second a down
     firstWindow->consumeMotionMove();
     secondWindow->consumeMotionDown();
@@ -4471,19 +4382,16 @@
 
     // The rest of the dispatch should proceed as normal
     // Send pointer up to the second window
-    NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {pointInFirst, pointInSecond});
-    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst, pointInSecond}));
     // The first window gets MOVE and the second gets pointer up
     firstWindow->consumeMotionMove();
     secondWindow->consumeMotionUp();
 
     // Send up event to the first window
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first window gets nothing and the second gets up
     firstWindow->consumeMotionUp();
     secondWindow->assertNoEvents();
@@ -4618,8 +4526,7 @@
 
     window->consumeFocusEvent(true);
 
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
 
     // Window should receive key down event.
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -4632,8 +4539,7 @@
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
     mDispatcher->waitForIdle();
 
     window->assertNoEvents();
@@ -4648,13 +4554,10 @@
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
     // Send key
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
     // Send motion
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
 
     // Window should receive only the motion event
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -4681,19 +4584,17 @@
     PointF pointInSecond = {300, 600};
 
     // Send down to the first window
-    NotifyMotionArgs firstDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {pointInFirst});
-    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst}));
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send down to the second window
-    NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {pointInFirst, pointInSecond});
-    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {pointInFirst, pointInSecond}));
     // The first window gets a move and the second a down
     firstWindow->consumeMotionMove();
     secondWindow->consumeMotionDown();
@@ -4703,16 +4604,14 @@
             generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
-    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    mDispatcher->notifyMotion(pointerUpMotionArgs);
     // The first window gets move and the second gets cancel.
     firstWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
     secondWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
 
     // Send up event.
-    NotifyMotionArgs upMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&upMotionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT));
     // The first window gets up and the second gets nothing.
     firstWindow->consumeMotionUp();
     secondWindow->assertNoEvents();
@@ -4934,7 +4833,7 @@
             generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                ADISPLAY_ID_DEFAULT);
 
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(motionArgs);
     // Window should receive motion down event.
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 
@@ -4944,7 +4843,7 @@
     motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
                                              motionArgs.pointerCoords[0].getX() - 10);
 
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(motionArgs);
     window->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
                          /*expectedFlags=*/0);
 }
@@ -5013,8 +4912,8 @@
 
     window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
 
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
-    mDispatcher->notifyKey(&keyArgs);
+    const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+    mDispatcher->notifyKey(keyArgs);
 
     InputEvent* event = window->consume();
     ASSERT_NE(event, nullptr);
@@ -5055,10 +4954,10 @@
 
     mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo});
 
-    NotifyMotionArgs motionArgs =
+    const NotifyMotionArgs motionArgs =
             generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(motionArgs);
 
     InputEvent* event = window->consume();
     ASSERT_NE(event, nullptr);
@@ -5355,17 +5254,17 @@
             {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
 
     // Use notifyMotion instead of injecting to avoid dealing with injection permissions
-    NotifyMotionArgs args = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                               ADISPLAY_ID_DEFAULT, {{50, 50}});
-    mDispatcher->notifyMotion(&args);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {{50, 50}}));
     slipperyExitWindow->consumeMotionDown();
     slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
     mDispatcher->setInputWindows(
             {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
 
-    args = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
-                              ADISPLAY_ID_DEFAULT, {{51, 51}});
-    mDispatcher->notifyMotion(&args);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {{51, 51}}));
 
     slipperyExitWindow->consumeMotionCancel();
 
@@ -5405,7 +5304,7 @@
         NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
         keyArgs.deviceId = deviceId;
         keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
-        mDispatcher->notifyKey(&keyArgs);
+        mDispatcher->notifyKey(keyArgs);
 
         // Window should receive key down event.
         mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -5428,7 +5327,7 @@
         NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
         keyArgs.deviceId = deviceId;
         keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
-        mDispatcher->notifyKey(&keyArgs);
+        mDispatcher->notifyKey(keyArgs);
 
         // Window should receive key down event.
         mWindow->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
@@ -5491,8 +5390,7 @@
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterDisableInputDevice) {
     sendAndConsumeKeyDown(DEVICE_ID);
     expectKeyRepeatOnce(/*repeatCount=*/1);
-    NotifyDeviceResetArgs args(/*id=*/10, /*eventTime=*/20, DEVICE_ID);
-    mDispatcher->notifyDeviceReset(&args);
+    mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
     mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT,
                           AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_LONG_PRESS);
     mWindow->assertNoEvents();
@@ -5744,10 +5642,10 @@
 
         motionArgs =
                 generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
-        mDispatcher->notifyMotion(&motionArgs);
+        mDispatcher->notifyMotion(motionArgs);
         motionArgs =
                 generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
-        mDispatcher->notifyMotion(&motionArgs);
+        mDispatcher->notifyMotion(motionArgs);
         ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
             const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
@@ -5761,9 +5659,9 @@
         NotifyKeyArgs keyArgs;
 
         keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
-        mDispatcher->notifyKey(&keyArgs);
+        mDispatcher->notifyKey(keyArgs);
         keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
-        mDispatcher->notifyKey(&keyArgs);
+        mDispatcher->notifyKey(keyArgs);
         ASSERT_TRUE(mDispatcher->waitForIdle());
 
         if (expectToBeFiltered) {
@@ -6113,9 +6011,8 @@
 
     void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints,
                                  std::vector<PointF> expectedPoints) {
-        NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
-                                                         ADISPLAY_ID_DEFAULT, touchedPoints);
-        mDispatcher->notifyMotion(&motionArgs);
+        mDispatcher->notifyMotion(generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
+                                                     ADISPLAY_ID_DEFAULT, touchedPoints));
 
         // Always consume from window1 since it's the window that has the InputReceiver
         consumeMotionEvent(mWindow1, action, expectedPoints);
@@ -6988,17 +6885,16 @@
 // The other window should not be affected by that.
 TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
     // Touch Window 1
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {FOCUSED_WINDOW_LOCATION}));
     mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
                                    ADISPLAY_ID_DEFAULT, /*flags=*/0);
 
     // Touch Window 2
-    motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                                    {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(
+            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION}));
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -7070,10 +6966,9 @@
     std::this_thread::sleep_for(10ms);
 
     // Touch unfocused window. This should force the pending key to get dropped.
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {UNFOCUSED_WINDOW_LOCATION}));
 
     // We do not consume the motion right away, because that would require dispatcher to first
     // process (== drop) the key event, and by that time, ANR will be raised.
@@ -7128,10 +7023,9 @@
 TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouches) {
     PointF touchedPoint = {10, 10};
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {touchedPoint}));
 
     mNoInputWindow->assertNoEvents();
     // Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have
@@ -7156,10 +7050,9 @@
 
     PointF touchedPoint = {10, 10};
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {touchedPoint}));
 
     mNoInputWindow->assertNoEvents();
     mBottomWindow->assertNoEvents();
@@ -7332,8 +7225,7 @@
     }
 
     void notifyPointerCaptureChanged(const PointerCaptureRequest& request) {
-        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request);
-        mDispatcher->notifyPointerCaptureChanged(&args);
+        mDispatcher->notifyPointerCaptureChanged(generatePointerCaptureChangedArgs(request));
     }
 
     PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
@@ -7509,10 +7401,9 @@
     }
 
     void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
-        NotifyMotionArgs args =
-                generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                   ADISPLAY_ID_DEFAULT, points);
-        mDispatcher->notifyMotion(&args);
+        mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                     AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                     points));
     }
 };
 
@@ -8322,27 +8213,22 @@
     window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
 
     // With the flag set, window should not get any input
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
     // With the flag cleared, the window should get input
     window->setDropInput(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
-    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
     window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     window->assertNoEvents();
 }
@@ -8368,27 +8254,22 @@
     window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
 
     // With the flag set, window should not get any input
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
     // With the flag cleared, the window should get input
     window->setDropInputIfObscured(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
 
-    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
     window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
     window->assertNoEvents();
 }
@@ -8414,26 +8295,21 @@
     window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
 
     // With the flag set, window should not get any input
-    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->assertNoEvents();
 
     // When the window is no longer obscured because it went on top, it should get input
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
 
-    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
     window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     window->assertNoEvents();
 }
@@ -9167,10 +9043,9 @@
     }
 
     void sendFingerEvent(int32_t action) {
-        NotifyMotionArgs motionArgs =
+        mDispatcher->notifyMotion(
                 generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
-                                   ADISPLAY_ID_DEFAULT, {PointF{20, 20}});
-        mDispatcher->notifyMotion(&motionArgs);
+                                   ADISPLAY_ID_DEFAULT, {PointF{20, 20}}));
     }
 
     void sendStylusEvent(int32_t action) {
@@ -9178,7 +9053,7 @@
                 generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
                                    ADISPLAY_ID_DEFAULT, {PointF{30, 40}});
         motionArgs.pointerProperties[0].toolType = ToolType::STYLUS;
-        mDispatcher->notifyMotion(&motionArgs);
+        mDispatcher->notifyMotion(motionArgs);
     }
 };
 
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index ae30006..ad48a79 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -50,12 +50,12 @@
     mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
 }
 
-std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
-    if (!changes ||
-        (changes &
-         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-          InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
-          InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
+std::list<NotifyArgs> InputMapperTest::configureDevice(ConfigurationChanges changes) {
+    using namespace ftl::flag_operators;
+    if (!changes.any() ||
+        (changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
+                     InputReaderConfiguration::Change::POINTER_CAPTURE |
+                     InputReaderConfiguration::Change::DEVICE_TYPE))) {
         mReader->requestRefreshConfiguration(changes);
         mReader->loopOnce();
     }
@@ -94,7 +94,7 @@
                                                    ViewportType viewportType) {
     mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
                                     uniqueId, physicalPort, viewportType);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 }
 
 void InputMapperTest::clearViewports() {
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 63ca44c..d969034 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -54,14 +54,15 @@
     void TearDown() override;
 
     void addConfigurationProperty(const char* key, const char* value);
-    std::list<NotifyArgs> configureDevice(uint32_t changes);
+    std::list<NotifyArgs> configureDevice(ConfigurationChanges changes);
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
                                            const std::string& location, int32_t eventHubId,
                                            ftl::Flags<InputDeviceClass> classes, int bus = 0);
     template <class T, typename... Args>
     T& addMapperAndConfigure(Args... args) {
-        T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
-        configureDevice(0);
+        T& mapper =
+                mDevice->addMapper<T>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), args...);
+        configureDevice(/*changes=*/{});
         std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
         resetArgList += mapper.reset(ARBITRARY_TIME);
         // Loop the reader to flush the input listener queue.
diff --git a/services/inputflinger/tests/InputProcessor_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp
index 0ffdef9..3b7cbfa 100644
--- a/services/inputflinger/tests/InputProcessor_test.cpp
+++ b/services/inputflinger/tests/InputProcessor_test.cpp
@@ -72,7 +72,7 @@
     // Create a basic configuration change and send to processor
     NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
 
-    mProcessor->notifyConfigurationChanged(&args);
+    mProcessor->notifyConfigurationChanged(args);
     NotifyConfigurationChangedArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
@@ -85,10 +85,8 @@
                        AKEY_EVENT_ACTION_DOWN, /*flags=*/4, AKEYCODE_HOME, /*scanCode=*/5,
                        AMETA_NONE, /*downTime=*/6);
 
-    mProcessor->notifyKey(&args);
-    NotifyKeyArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs));
-    ASSERT_EQ(args, outArgs);
+    mProcessor->notifyKey(args);
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(testing::Eq(args)));
 }
 
 /**
@@ -97,10 +95,8 @@
  */
 TEST_F(InputProcessorTest, SendToNextStage_NotifyMotionArgs) {
     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
-    mProcessor->notifyMotion(&motionArgs);
-    NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(motionArgs, args);
+    mProcessor->notifyMotion(motionArgs);
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(testing::Eq(motionArgs)));
 }
 
 /**
@@ -111,7 +107,7 @@
     NotifySwitchArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*policyFlags=*/3,
                           /*switchValues=*/4, /*switchMask=*/5);
 
-    mProcessor->notifySwitch(&args);
+    mProcessor->notifySwitch(args);
     NotifySwitchArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
@@ -124,7 +120,7 @@
 TEST_F(InputProcessorTest, SendToNextStage_NotifyDeviceResetArgs) {
     NotifyDeviceResetArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*deviceId=*/3);
 
-    mProcessor->notifyDeviceReset(&args);
+    mProcessor->notifyDeviceReset(args);
     NotifyDeviceResetArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2609f1f..d56db11 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -171,8 +171,9 @@
 
     std::optional<DisplayViewport> mViewport;
 public:
-    FakeInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : InputMapper(deviceContext),
+    FakeInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig,
+                    uint32_t sources)
+          : InputMapper(deviceContext, readerConfig),
             mSources(sources),
             mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
             mMetaState(0),
@@ -264,13 +265,13 @@
     }
 
     std::list<NotifyArgs> reconfigure(nsecs_t, const InputReaderConfiguration& config,
-                                      uint32_t changes) override {
+                                      ConfigurationChanges changes) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
         // Find the associated viewport if exist.
         const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
-        if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        if (displayPort && changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
             mViewport = config.getDisplayViewportByPort(*displayPort);
         }
 
@@ -609,12 +610,12 @@
 
     void disableDevice(int32_t deviceId) {
         mFakePolicy->addDisabledDevice(deviceId);
-        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::ENABLED_STATE);
     }
 
     void enableDevice(int32_t deviceId) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::ENABLED_STATE);
     }
 
     FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
@@ -623,7 +624,9 @@
                                                   uint32_t sources,
                                                   const PropertyMap* configuration) {
         std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
-        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
+        FakeInputMapper& mapper =
+                device->addMapper<FakeInputMapper>(eventHubId,
+                                                   mFakePolicy->getReaderConfiguration(), sources);
         mReader->pushNextDevice(device);
         addDevice(eventHubId, name, classes, configuration);
         return mapper;
@@ -675,8 +678,10 @@
     // Add two subdevices to device
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
-    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[0], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
 
     // Push same device instance for next device to be added, so they'll have same identifier.
     mReader->pushNextDevice(device);
@@ -696,8 +701,10 @@
     // Add two subdevices to device
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
-    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[0], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
 
     // Push same device instance for next device to be added, so they'll have same identifier.
     mReader->pushNextDevice(device);
@@ -722,7 +729,8 @@
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubId, mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
@@ -968,7 +976,8 @@
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubId, mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
@@ -1001,7 +1010,8 @@
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubId, mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
@@ -1017,7 +1027,8 @@
     const char* DEVICE_LOCATION = "USB1";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
     FakeInputMapper& mapper =
-            device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
+            device->addMapper<FakeInputMapper>(eventHubId, mFakePolicy->getReaderConfiguration(),
+                                               AINPUT_SOURCE_TOUCHSCREEN);
     mReader->pushNextDevice(device);
 
     const uint8_t hdmi1 = 1;
@@ -1032,7 +1043,7 @@
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     ui::ROTATION_0, /*isActive=*/true, "local:1", hdmi1,
                                     ViewportType::EXTERNAL);
-    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
     mReader->loopOnce();
 
     // Add the device, and make sure all of the callbacks are triggered.
@@ -1060,8 +1071,10 @@
     constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
-    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[0], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], mFakePolicy->getReaderConfiguration(),
+                                       AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
@@ -1102,9 +1115,13 @@
     // Add two subdevices to device
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     FakeInputMapper& mapperDevice1 =
-            device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+            device->addMapper<FakeInputMapper>(eventHubIds[0],
+                                               mFakePolicy->getReaderConfiguration(),
+                                               AINPUT_SOURCE_KEYBOARD);
     FakeInputMapper& mapperDevice2 =
-            device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+            device->addMapper<FakeInputMapper>(eventHubIds[1],
+                                               mFakePolicy->getReaderConfiguration(),
+                                               AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
@@ -1125,29 +1142,30 @@
     NotifyPointerCaptureChangedArgs args;
 
     auto request = mFakePolicy->setPointerCapture(true);
-    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
     ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
     ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
 
     mFakePolicy->setPointerCapture(false);
-    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
     ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
 
     // Verify that the Pointer Capture state is not updated when the configuration value
     // does not change.
-    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasNotCalled();
 }
 
 class FakeVibratorInputMapper : public FakeInputMapper {
 public:
-    FakeVibratorInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : FakeInputMapper(deviceContext, sources) {}
+    FakeVibratorInputMapper(InputDeviceContext& deviceContext,
+                            const InputReaderConfiguration& readerConfig, uint32_t sources)
+          : FakeInputMapper(deviceContext, readerConfig, sources) {}
 
     std::vector<int32_t> getVibratorIds() override { return getDeviceContext().getVibratorIds(); }
 };
@@ -1160,7 +1178,9 @@
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
     FakeVibratorInputMapper& mapper =
-            device->addMapper<FakeVibratorInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+            device->addMapper<FakeVibratorInputMapper>(eventHubId,
+                                                       mFakePolicy->getReaderConfiguration(),
+                                                       AINPUT_SOURCE_KEYBOARD);
     mReader->pushNextDevice(device);
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
@@ -1507,7 +1527,7 @@
                                       ViewportType viewportType) {
         mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /*isActive=*/true,
                                         uniqueId, physicalPort, viewportType);
-        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
     }
 
     void assertReceivedMotion(int32_t action, const std::vector<Point>& points) {
@@ -2050,7 +2070,7 @@
 TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
     TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
     TestFixture::mReader->requestRefreshConfiguration(
-            InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
+            InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
 
     const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
     const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
@@ -2321,7 +2341,7 @@
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
     // Configuration.
     InputReaderConfiguration config;
-    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
 
     // Reset.
     unused += mDevice->reset(ARBITRARY_TIME);
@@ -2364,7 +2384,8 @@
     mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "key", "value");
 
     FakeInputMapper& mapper1 =
-            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                AINPUT_SOURCE_KEYBOARD);
     mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     mapper1.setMetaState(AMETA_ALT_ON);
     mapper1.addSupportedKeyCode(AKEYCODE_A);
@@ -2376,11 +2397,12 @@
     mapper1.setSwitchState(4, AKEY_STATE_DOWN);
 
     FakeInputMapper& mapper2 =
-            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                AINPUT_SOURCE_TOUCHSCREEN);
     mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
-    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
 
     std::optional<std::string> propertyValue = mDevice->getConfiguration().getString("key");
     ASSERT_TRUE(propertyValue.has_value())
@@ -2457,11 +2479,13 @@
 // 1. Device is disabled if the viewport corresponding to the associated display is not found
 // 2. Device is disabled when setEnabled API is called
 TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
-    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                        AINPUT_SOURCE_TOUCHSCREEN);
 
     // First Configuration.
     std::list<NotifyArgs> unused =
-            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               /*changes=*/{});
 
     // Device should be enabled by default.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -2472,7 +2496,7 @@
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     // Device should be disabled because it is associated with a specific display via
     // input port <-> display port association, but the corresponding display is not found
     ASSERT_FALSE(mDevice->isEnabled());
@@ -2482,34 +2506,36 @@
                                     ui::ROTATION_0, /*isActive=*/true, UNIQUE_ID, hdmi,
                                     ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
+                                 InputReaderConfiguration::Change::ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
 TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {
     // Device should be enabled by default.
     mFakePolicy->clearViewports();
-    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                        AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
-            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               /*changes=*/{});
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled because it is associated with a specific display, but the
     // corresponding display is not found.
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should be enabled when a display is found.
@@ -2517,33 +2543,35 @@
                                     ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
+                                 InputReaderConfiguration::Change::ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
 TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
     mFakePolicy->clearViewports();
-    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                        AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
-            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               /*changes=*/{});
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
@@ -2556,7 +2584,7 @@
     mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
 
     InputDevice device(mReader->getContext(), /*id=*/1, /*generation=*/2, /*identifier=*/{});
-    device.addEventHubDevice(TEST_EVENTHUB_ID, /*populateMappers=*/true);
+    device.addEventHubDevice(TEST_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
     device.removeEventHubDevice(TEST_EVENTHUB_ID);
     std::string dumpStr, eventHubDevStr;
     device.dump(dumpStr, eventHubDevStr);
@@ -3392,11 +3420,13 @@
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     KeyboardInputMapper& mapper2 =
-            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+                                                    mFakePolicy->getReaderConfiguration(),
+                                                    AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                               /*changes=*/0);
+                               /*changes=*/{});
     unused += device2->reset(ARBITRARY_TIME);
 
     // Prepared displays and associated info.
@@ -3409,7 +3439,7 @@
 
     // No associated display viewport found, should disable the device.
     unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_FALSE(device2->isEnabled());
 
     // Prepare second display.
@@ -3420,7 +3450,7 @@
                                  SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
     // Default device will reconfigure above, need additional reconfiguration for another device.
     unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // Device should be enabled after the associated display is found.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -3502,11 +3532,13 @@
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper2 =
-            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+                                                    mFakePolicy->getReaderConfiguration(),
+                                                    AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                               /*changes=*/0);
+                               /*changes=*/{});
     unused += device2->reset(ARBITRARY_TIME);
 
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
@@ -3563,11 +3595,13 @@
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper2 =
-            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+                                                    mFakePolicy->getReaderConfiguration(),
+                                                    AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                               /*changes=*/0);
+                               /*changes=*/{});
     unused += device2->reset(ARBITRARY_TIME);
 
     // Initial metastate is AMETA_NONE.
@@ -3636,7 +3670,7 @@
 
     // Disable device, it should synthesize cancellation events for down events.
     mFakePolicy->addDisabledDevice(DEVICE_ID);
-    configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    configureDevice(InputReaderConfiguration::Change::ENABLED_STATE);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -3646,21 +3680,32 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
-    mDevice->addMapper<KeyboardInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+    mDevice->addMapper<KeyboardInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                            AINPUT_SOURCE_KEYBOARD,
                                             AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     std::list<NotifyArgs> unused =
-            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               /*changes=*/{});
 
+    uint32_t generation = mReader->getContext()->getGeneration();
     mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
 
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION);
+                                 InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
 
     InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
     ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
               deviceInfo.getKeyboardLayoutInfo()->languageTag);
     ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
               deviceInfo.getKeyboardLayoutInfo()->layoutType);
+    ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
+
+    // Call change layout association with the same values: Generation shouldn't change
+    generation = mReader->getContext()->getGeneration();
+    mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+    ASSERT_TRUE(mReader->getContext()->getGeneration() == generation);
 }
 
 TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) {
@@ -3671,7 +3716,7 @@
     addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     InputReaderConfiguration config;
-    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
 
     ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag);
     ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
@@ -4477,7 +4522,7 @@
     // and events are generated the usual way.
     const uint32_t generation = mReader->getContext()->getGeneration();
     mFakePolicy->setPointerCapture(false);
-    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4526,7 +4571,7 @@
 
     // Enable Pointer Capture
     mFakePolicy->setPointerCapture(true);
-    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     NotifyPointerCaptureChangedArgs captureArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
     ASSERT_TRUE(captureArgs.request.enable);
@@ -4568,7 +4613,7 @@
 
     // Enable Pointer Capture.
     mFakePolicy->setPointerCapture(true);
-    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     NotifyPointerCaptureChangedArgs captureArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
     ASSERT_TRUE(captureArgs.request.enable);
@@ -4594,7 +4639,7 @@
     // The InputDevice is not associated with any display.
     prepareSecondaryDisplay();
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     mFakePointerController->setPosition(100, 200);
@@ -4621,7 +4666,7 @@
     prepareSecondaryDisplay();
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     mFakePointerController->setPosition(100, 200);
@@ -4646,7 +4691,7 @@
     // Associate the InputDevice with the secondary display.
     prepareSecondaryDisplay();
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // The mapper should not generate any events because it is associated with a display that is
     // different from the pointer display.
@@ -5813,7 +5858,7 @@
     viewport->physicalRight = 30;
     viewport->physicalBottom = 610;
     mFakePolicy->updateViewport(*viewport);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // Start the touch.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
@@ -6538,7 +6583,7 @@
     auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     viewport->isActive = false;
     mFakePolicy->updateViewport(*viewport);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // We should receive a cancel event for the ongoing gesture.
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6563,7 +6608,7 @@
     // Make the viewport active again. The device should resume processing events.
     viewport->isActive = true;
     mFakePolicy->updateViewport(*viewport);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // The device is reset because it changes back to direct mode, without generating any events.
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
@@ -6731,7 +6776,7 @@
     // Send update to the mapper.
     std::list<NotifyArgs> unused2 =
             mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                               InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/);
+                               InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/);
 
     // Check whether device type update was successful.
     ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
@@ -6752,7 +6797,7 @@
     viewport->physicalRight = DISPLAY_WIDTH / 2;
     viewport->physicalBottom = DISPLAY_HEIGHT / 2;
     mFakePolicy->updateViewport(*viewport);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
@@ -6841,7 +6886,7 @@
         v.uniqueId = UNIQUE_ID;
         v.type = ViewportType::INTERNAL;
         mFakePolicy->updateViewport(v);
-        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+        configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     }
 
     void assertReceivedMove(const Point& point) {
@@ -7201,7 +7246,7 @@
         mStylusState.pressure = 0.f;
         mStylusState.toolType = ToolType::STYLUS;
         mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
-        configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+        configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
         processExternalStylusState(mapper);
         return mapper;
     }
@@ -9257,7 +9302,7 @@
     // Don't set touch.enableForInactiveViewport to verify the default behavior.
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                     /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9277,7 +9322,7 @@
     addConfigurationProperty("touch.enableForInactiveViewport", "1");
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                     /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9299,7 +9344,7 @@
     ASSERT_TRUE(optionalDisplayViewport.has_value());
     DisplayViewport displayViewport = *optionalDisplayViewport;
 
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9315,7 +9360,7 @@
     // Deactivate display viewport
     displayViewport.isActive = false;
     ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // The ongoing touch should be canceled immediately
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -9330,7 +9375,7 @@
     // Reactivate display viewport
     displayViewport.isActive = true;
     ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     // Finger move again starts new gesture
     x += 10, y += 10;
@@ -9368,10 +9413,12 @@
                                             String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
+    MultiTouchInputMapper& mapper2 =
+            device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID,
+                                                      mFakePolicy->getReaderConfiguration());
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                               /*changes=*/0);
+                               /*changes=*/{});
     unused += device2->reset(ARBITRARY_TIME);
 
     // Setup PointerController.
@@ -9392,8 +9439,8 @@
 
     // Default device will reconfigure above, need additional reconfiguration for another device.
     unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-                                         InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+                                 InputReaderConfiguration::Change::DISPLAY_INFO |
+                                         InputReaderConfiguration::Change::SHOW_TOUCHES);
 
     // Two fingers down at default display.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -9424,7 +9471,7 @@
     // Disable the show touches configuration and ensure the spots are cleared.
     mFakePolicy->setShowTouches(false);
     unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                                 InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+                                 InputReaderConfiguration::Change::SHOW_TOUCHES);
 
     ASSERT_TRUE(fakePointerController->getSpots().empty());
 }
@@ -10334,7 +10381,7 @@
 
     // non captured touchpad should be a mouse source
     mFakePolicy->setPointerCapture(false);
-    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
@@ -10419,7 +10466,7 @@
 
     // captured touchpad should be a touchpad device
     mFakePolicy->setPointerCapture(true);
-    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
@@ -10805,7 +10852,7 @@
     auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     viewport->isActive = false;
     mFakePolicy->updateViewport(*viewport);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
             AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
                   WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
@@ -10913,14 +10960,6 @@
         mFakePolicy.clear();
     }
 
-    std::list<NotifyArgs> configureDevice(uint32_t changes) {
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-            mReader->requestRefreshConfiguration(changes);
-            mReader->loopOnce();
-        }
-        return 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,
                                            ftl::Flags<InputDeviceClass> classes) {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index ac1dc05..fc917dd 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -168,47 +168,47 @@
 }
 
 template <class NotifyArgsType>
-void TestInputListener::addToQueue(const NotifyArgsType* args) {
+void TestInputListener::addToQueue(const NotifyArgsType& args) {
     std::scoped_lock<std::mutex> lock(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
-    queue.push_back(*args);
+    queue.push_back(args);
     mCondition.notify_all();
 }
 
 void TestInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
-    addToQueue<NotifyInputDevicesChangedArgs>(&args);
+    addToQueue<NotifyInputDevicesChangedArgs>(args);
 }
 
-void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
     addToQueue<NotifyConfigurationChangedArgs>(args);
 }
 
-void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
     addToQueue<NotifyDeviceResetArgs>(args);
 }
 
-void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+void TestInputListener::notifyKey(const NotifyKeyArgs& args) {
     addToQueue<NotifyKeyArgs>(args);
 }
 
-void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+void TestInputListener::notifyMotion(const NotifyMotionArgs& args) {
     addToQueue<NotifyMotionArgs>(args);
 }
 
-void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+void TestInputListener::notifySwitch(const NotifySwitchArgs& args) {
     addToQueue<NotifySwitchArgs>(args);
 }
 
-void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
     addToQueue<NotifyPointerCaptureChangedArgs>(args);
 }
 
-void TestInputListener::notifySensor(const NotifySensorArgs* args) {
+void TestInputListener::notifySensor(const NotifySensorArgs& args) {
     addToQueue<NotifySensorArgs>(args);
 }
 
-void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs& args) {
     addToQueue<NotifyVibratorStateArgs>(args);
 }
 
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index da2cab3..deb6048 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -77,25 +77,25 @@
     void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {});
 
     template <class NotifyArgsType>
-    void addToQueue(const NotifyArgsType* args);
+    void addToQueue(const NotifyArgsType& args);
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
 
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
 
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
 
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
+    virtual void notifyKey(const NotifyKeyArgs& args) override;
 
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
+    virtual void notifyMotion(const NotifyMotionArgs& args) override;
 
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
+    virtual void notifySwitch(const NotifySwitchArgs& args) override;
 
-    virtual void notifySensor(const NotifySensorArgs* args) override;
+    virtual void notifySensor(const NotifySensorArgs& args) override;
 
-    virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+    virtual void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
 
-    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
     std::mutex mLock;
     std::condition_variable mCondition;
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index be731b1..1fff2c7 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -421,7 +421,7 @@
     // Create a basic configuration change and send to blocker
     NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
 
-    mBlocker->notifyConfigurationChanged(&args);
+    mBlocker->notifyConfigurationChanged(args);
     NotifyConfigurationChangedArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
@@ -438,10 +438,8 @@
                        AKEY_EVENT_ACTION_DOWN, /*flags=*/4, AKEYCODE_HOME, /*scanCode=*/5,
                        AMETA_NONE, /*downTime=*/6);
 
-    mBlocker->notifyKey(&args);
-    NotifyKeyArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs));
-    ASSERT_EQ(args, outArgs);
+    mBlocker->notifyKey(args);
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(testing::Eq(args)));
 }
 
 /**
@@ -452,10 +450,8 @@
 TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) {
     NotifyMotionArgs motionArgs =
             generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
-    mBlocker->notifyMotion(&motionArgs);
-    NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(motionArgs, args);
+    mBlocker->notifyMotion(motionArgs);
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(testing::Eq(motionArgs)));
 }
 
 /**
@@ -466,7 +462,7 @@
     NotifySwitchArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*policyFlags=*/3,
                           /*switchValues=*/4, /*switchMask=*/5);
 
-    mBlocker->notifySwitch(&args);
+    mBlocker->notifySwitch(args);
     NotifySwitchArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
@@ -479,7 +475,7 @@
 TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) {
     NotifyDeviceResetArgs args(/*sequenceNum=*/1, /*eventTime=*/2, DEVICE_ID);
 
-    mBlocker->notifyDeviceReset(&args);
+    mBlocker->notifyDeviceReset(args);
     NotifyDeviceResetArgs outArgs;
     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
@@ -491,17 +487,12 @@
  * a crash due to inconsistent event stream could have occurred.
  */
 TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) {
-    NotifyMotionArgs args;
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}})));
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}})));
-    NotifyDeviceResetArgs resetArgs(/*sequenceNum=*/1, /*eventTime=*/3, DEVICE_ID);
-    mBlocker->notifyDeviceReset(&resetArgs);
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
+    mBlocker->notifyDeviceReset({/*sequenceNum=*/1, /*eventTime=*/3, DEVICE_ID});
     // Start a new gesture with a DOWN event, even though the previous event stream was incomplete.
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, DOWN, {{7, 8, 9}})));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, DOWN, {{7, 8, 9}}));
 }
 
 TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenStylusSourceWithFingerToolIsReceived) {
@@ -509,7 +500,7 @@
     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}});
     args.pointerProperties[0].toolType = ToolType::FINGER;
     args.source = AINPUT_SOURCE_STYLUS;
-    mBlocker->notifyMotion(&args);
+    mBlocker->notifyMotion(args);
 }
 
 /**
@@ -517,48 +508,41 @@
  * UnwantedInteractionBlocker has not changed, there should not be a reset.
  */
 TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) {
-    NotifyMotionArgs args;
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}})));
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}})));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
 
     // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice.
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
 
     // The MOVE event continues the gesture that started before 'devices changed', so it should not
     // cause a crash.
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, MOVE, {{7, 8, 9}})));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, MOVE, {{7, 8, 9}}));
 }
 
 /**
  * Send a touch event, and then a stylus event. Make sure that both work.
  */
 TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) {
-    NotifyMotionArgs args;
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
-    mBlocker->notifyMotion(&args);
-    args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}});
-    mBlocker->notifyMotion(&args);
-    args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}});
-    mBlocker->notifyMotion(&args);
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}}));
 
     // Now touch down stylus
+    NotifyMotionArgs args;
     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/3, DOWN, {{10, 20, 30}});
     args.pointerProperties[0].toolType = ToolType::STYLUS;
     args.source |= AINPUT_SOURCE_STYLUS;
-    mBlocker->notifyMotion(&args);
+    mBlocker->notifyMotion(args);
     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/4, MOVE, {{40, 50, 60}});
     args.pointerProperties[0].toolType = ToolType::STYLUS;
     args.source |= AINPUT_SOURCE_STYLUS;
-    mBlocker->notifyMotion(&args);
+    mBlocker->notifyMotion(args);
     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/5, UP, {{40, 50, 60}});
     args.pointerProperties[0].toolType = ToolType::STYLUS;
     args.source |= AINPUT_SOURCE_STYLUS;
-    mBlocker->notifyMotion(&args);
+    mBlocker->notifyMotion(args);
 }
 
 /**
@@ -569,16 +553,13 @@
  */
 TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) {
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
-    mBlocker->notifyMotion(&args1);
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
     std::thread dumpThread([this]() {
         std::string dump;
         mBlocker->dump(dump);
     });
-    NotifyMotionArgs args2 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}});
-    mBlocker->notifyMotion(&args2);
-    NotifyMotionArgs args3 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}});
-    mBlocker->notifyMotion(&args3);
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}}));
     dumpThread.join();
 }
 
@@ -589,20 +570,17 @@
 TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
     // Small touch down
-    NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
-    mBlocker->notifyMotion(&args1);
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Large touch oval on the next move
-    NotifyMotionArgs args2 =
-            generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
-    mBlocker->notifyMotion(&args2);
+    mBlocker->notifyMotion(
+            generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}));
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the touch to force the model to decide on whether it's a palm
-    NotifyMotionArgs args3 =
-            generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
-    mBlocker->notifyMotion(&args3);
+    mBlocker->notifyMotion(
+            generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}));
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(CANCEL));
 }
 
@@ -618,14 +596,14 @@
     mBlocker->notifyInputDevicesChanged(deviceChangedArgs);
     NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
     args1.pointerProperties[0].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args1);
+    mBlocker->notifyMotion(args1);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
     NotifyMotionArgs args2 =
             generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
     args2.pointerProperties[0].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args2);
+    mBlocker->notifyMotion(args2);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
@@ -633,7 +611,7 @@
     NotifyMotionArgs args3 =
             generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
     args3.pointerProperties[0].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args3);
+    mBlocker->notifyMotion(args3);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
 }
 
@@ -649,28 +627,28 @@
 
     // Touch down
     NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
-    mBlocker->notifyMotion(&args1);
+    mBlocker->notifyMotion(args1);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Stylus pointer down
     NotifyMotionArgs args2 = generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, POINTER_1_DOWN,
                                                 {{1, 2, 3}, {10, 20, 30}});
     args2.pointerProperties[1].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args2);
+    mBlocker->notifyMotion(args2);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(POINTER_1_DOWN));
 
     // Large touch oval on the next finger move
     NotifyMotionArgs args3 = generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, MOVE,
                                                 {{1, 2, 300}, {11, 21, 30}});
     args3.pointerProperties[1].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args3);
+    mBlocker->notifyMotion(args3);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the finger pointer. It should be canceled due to the heuristic filter.
     NotifyMotionArgs args4 = generateMotionArgs(/*downTime=*/0, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
                                                 {{1, 2, 300}, {11, 21, 30}});
     args4.pointerProperties[1].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args4);
+    mBlocker->notifyMotion(args4);
     mTestListener.assertNotifyMotionWasCalled(
             AllOf(WithMotionAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
 
@@ -678,7 +656,7 @@
             generateMotionArgs(/*downTime=*/0, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
     args5.pointerProperties[0].id = args4.pointerProperties[1].id;
     args5.pointerProperties[0].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args5);
+    mBlocker->notifyMotion(args5);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the stylus pointer
@@ -686,7 +664,7 @@
             generateMotionArgs(/*downTime=*/0, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
     args6.pointerProperties[0].id = args4.pointerProperties[1].id;
     args6.pointerProperties[0].toolType = ToolType::STYLUS;
-    mBlocker->notifyMotion(&args6);
+    mBlocker->notifyMotion(args6);
     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
 }
 
@@ -700,17 +678,15 @@
     ScopedSilentDeath _silentDeath;
     NotifyMotionArgs args;
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}})));
-    mBlocker->notifyMotion(
-            &(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}})));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
+    mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
     NotifyDeviceResetArgs resetArgs(/*sequenceNum=*/1, /*eventTime=*/3, DEVICE_ID);
-    mBlocker->notifyDeviceReset(&resetArgs);
+    mBlocker->notifyDeviceReset(resetArgs);
     // Sending MOVE without a DOWN -> should crash!
     ASSERT_DEATH(
             {
-                mBlocker->notifyMotion(&(args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/4,
-                                                                   MOVE, {{7, 8, 9}})));
+                mBlocker->notifyMotion(
+                        generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, MOVE, {{7, 8, 9}}));
             },
             "Could not find slot");
 }
@@ -720,9 +696,13 @@
  */
 TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) {
     ScopedSilentDeath _silentDeath;
-    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 2, 3}});
     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
-    ASSERT_DEATH({ mBlocker->notifyMotion(&args); }, "Could not find slot");
+    ASSERT_DEATH(
+            {
+                mBlocker->notifyMotion(
+                        generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 2, 3}}));
+            },
+            "Could not find slot");
 }
 
 class PalmRejectorTest : public testing::Test {
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 28873a3..8098ef2 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -38,8 +38,8 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>();
     auto policyConfig = fuzzer.getPolicyConfig();
+    CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(policyConfig);
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
@@ -53,12 +53,14 @@
                 [&]() -> void {
                     std::list<NotifyArgs> unused =
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
-                                               fdp->ConsumeIntegral<int32_t>());
+                                               InputReaderConfiguration::Change(
+                                                       fdp->ConsumeIntegral<int32_t>()));
                 },
                 [&]() -> void {
                     // Need to reconfigure with 0 or you risk a NPE.
                     std::list<NotifyArgs> unused =
-                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+                                               InputReaderConfiguration::Change(0));
                     InputDeviceInfo info;
                     mapper.populateDeviceInfo(info);
                 },
@@ -71,7 +73,8 @@
 
                     // Need to reconfigure with 0 or you risk a NPE.
                     std::list<NotifyArgs> unused =
-                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+                                               InputReaderConfiguration::Change(0));
                     RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
                                       fdp->ConsumeIntegral<nsecs_t>(),
                                       fdp->ConsumeIntegral<int32_t>(),
@@ -90,7 +93,8 @@
                 [&]() -> void {
                     // Need to reconfigure with 0 or you risk a NPE.
                     std::list<NotifyArgs> unused =
-                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+                                               InputReaderConfiguration::Change(0));
                     mapper.getAssociatedDisplayId();
                 },
         })();
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index d42d11c..84ac0fd 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -59,7 +59,7 @@
     void configureDevice() {
         nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
         std::list<NotifyArgs> out;
-        out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, 0);
+        out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
         out += mFuzzDevice->reset(arbitraryTime);
         for (const NotifyArgs& args : out) {
             mFuzzListener.notify(args);
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index 6617f65..f8ebc97 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -67,51 +67,42 @@
         fdp.PickValueInArray<std::function<void()>>({
                 [&]() -> void {
                     // SendToNextStage_NotifyConfigurationChangedArgs
-                    NotifyConfigurationChangedArgs
-                            args(/*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
-                                 /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>());
-                    mClassifier->notifyConfigurationChanged(&args);
+                    mClassifier->notifyConfigurationChanged(
+                            {/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
+                             /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>()});
                 },
                 [&]() -> void {
                     // SendToNextStage_NotifyKeyArgs
                     const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
                     const nsecs_t readTime =
                             eventTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
-                    NotifyKeyArgs keyArgs(/*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
-                                          eventTime, readTime,
-                                          /*deviceId=*/fdp.ConsumeIntegral<int32_t>(),
-                                          AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
-                                          /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
-                                          AKEY_EVENT_ACTION_DOWN,
-                                          /*flags=*/fdp.ConsumeIntegral<int32_t>(), AKEYCODE_HOME,
-                                          /*scanCode=*/fdp.ConsumeIntegral<int32_t>(), AMETA_NONE,
-                                          /*downTime=*/fdp.ConsumeIntegral<nsecs_t>());
-
-                    mClassifier->notifyKey(&keyArgs);
+                    mClassifier->notifyKey({/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
+                                            eventTime, readTime,
+                                            /*deviceId=*/fdp.ConsumeIntegral<int32_t>(),
+                                            AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
+                                            /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
+                                            AKEY_EVENT_ACTION_DOWN,
+                                            /*flags=*/fdp.ConsumeIntegral<int32_t>(), AKEYCODE_HOME,
+                                            /*scanCode=*/fdp.ConsumeIntegral<int32_t>(), AMETA_NONE,
+                                            /*downTime=*/fdp.ConsumeIntegral<nsecs_t>()});
                 },
                 [&]() -> void {
                     // SendToNextStage_NotifyMotionArgs
-                    NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
-                    mClassifier->notifyMotion(&motionArgs);
+                    mClassifier->notifyMotion(generateFuzzedMotionArgs(fdp));
                 },
                 [&]() -> void {
                     // SendToNextStage_NotifySwitchArgs
-                    NotifySwitchArgs switchArgs(/*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
-                                                /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>(),
-                                                /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
-                                                /*switchValues=*/fdp.ConsumeIntegral<uint32_t>(),
-                                                /*switchMask=*/fdp.ConsumeIntegral<uint32_t>());
-
-                    mClassifier->notifySwitch(&switchArgs);
+                    mClassifier->notifySwitch({/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
+                                               /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>(),
+                                               /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
+                                               /*switchValues=*/fdp.ConsumeIntegral<uint32_t>(),
+                                               /*switchMask=*/fdp.ConsumeIntegral<uint32_t>()});
                 },
                 [&]() -> void {
                     // SendToNextStage_NotifyDeviceResetArgs
-                    NotifyDeviceResetArgs resetArgs(
-                            /*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
-                            /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>(),
-                            /*deviceId=*/fdp.ConsumeIntegral<int32_t>());
-
-                    mClassifier->notifyDeviceReset(&resetArgs);
+                    mClassifier->notifyDeviceReset({/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
+                                                    /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>(),
+                                                    /*deviceId=*/fdp.ConsumeIntegral<int32_t>()});
                 },
                 [&]() -> void {
                     // InputClassifierConverterTest
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index baece3c..9223287 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -82,7 +82,7 @@
         return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags);
     }
 
-    void requestRefreshConfiguration(uint32_t changes) {
+    void requestRefreshConfiguration(ConfigurationChanges changes) {
         reader->requestRefreshConfiguration(changes);
     }
 
@@ -232,7 +232,8 @@
                                     fdp->ConsumeIntegral<uint32_t>(), keyCodes, outFlags.data());
                 },
                 [&]() -> void {
-                    reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>());
+                    reader->requestRefreshConfiguration(
+                            InputReaderConfiguration::Change(fdp->ConsumeIntegral<uint32_t>()));
                 },
                 [&]() -> void {
                     reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 00b44b5..616e870 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -44,10 +44,10 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    KeyboardInputMapper& mapper =
-            fuzzer.getMapper<KeyboardInputMapper>(fdp->ConsumeIntegral<uint32_t>(),
-                                                  fdp->ConsumeIntegral<int32_t>());
     auto policyConfig = fuzzer.getPolicyConfig();
+    KeyboardInputMapper& mapper =
+            fuzzer.getMapper<KeyboardInputMapper>(policyConfig, fdp->ConsumeIntegral<uint32_t>(),
+                                                  fdp->ConsumeIntegral<int32_t>());
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
@@ -65,7 +65,8 @@
                 [&]() -> void {
                     std::list<NotifyArgs> unused =
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
-                                               fdp->ConsumeIntegral<uint32_t>());
+                                               InputReaderConfiguration::Change(
+                                                       fdp->ConsumeIntegral<uint32_t>()));
                 },
                 [&]() -> void {
                     std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 0dc627a..1e44e0f 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -294,14 +294,14 @@
 class FuzzInputListener : public virtual InputListenerInterface {
 public:
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override {}
-    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override {}
-    void notifyKey(const NotifyKeyArgs* args) override {}
-    void notifyMotion(const NotifyMotionArgs* args) override {}
-    void notifySwitch(const NotifySwitchArgs* args) override {}
-    void notifySensor(const NotifySensorArgs* args) override{};
-    void notifyVibratorState(const NotifyVibratorStateArgs* args) override{};
-    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override {}
-    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override{};
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override {}
+    void notifyKey(const NotifyKeyArgs& args) override {}
+    void notifyMotion(const NotifyMotionArgs& args) override {}
+    void notifySwitch(const NotifySwitchArgs& args) override {}
+    void notifySensor(const NotifySensorArgs& args) override{};
+    void notifyVibratorState(const NotifyVibratorStateArgs& args) override{};
+    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override {}
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override{};
 };
 
 class FuzzInputReaderContext : public InputReaderContext {
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 70908ff..212462d 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -61,8 +61,8 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>();
     auto policyConfig = fuzzer.getPolicyConfig();
+    MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(policyConfig);
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
@@ -80,7 +80,8 @@
                 [&]() -> void {
                     std::list<NotifyArgs> unused =
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
-                                               fdp->ConsumeIntegral<uint32_t>());
+                                               InputReaderConfiguration::Change(
+                                                       fdp->ConsumeIntegral<uint32_t>()));
                 },
                 [&]() -> void {
                     std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index c4938f2..590207e 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -24,8 +24,8 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>();
     auto policyConfig = fuzzer.getPolicyConfig();
+    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(policyConfig);
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 608c53a..ccff1ec 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -19,6 +19,7 @@
 #include <optional>
 #include <ostream>
 #include <unordered_set>
+#include "ui/LayerStack.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -140,7 +141,7 @@
             ClientCompositionTargetSettings&) const = 0;
 
     // Called after the layer is displayed to update the presentation fence
-    virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>) = 0;
+    virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
 
     // Gets some kind of identifier for the layer for debug purposes.
     virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 12e063b..15e4577 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -49,7 +49,8 @@
                        std::optional<compositionengine::LayerFE::LayerSettings>(
                                compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
-    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>), (override));
+    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
+                (override));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index d64231f..793959c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1556,8 +1556,9 @@
             releaseFence =
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
-        layer->getLayerFE().onLayerDisplayed(
-                ftl::yield<FenceResult>(std::move(releaseFence)).share());
+        layer->getLayerFE()
+                .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
+                                  outputState.layerFilter.layerStack);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1565,7 +1566,8 @@
     // supply them with the present fence.
     for (auto& weakLayer : mReleasedLayers) {
         if (const auto layer = weakLayer.promote()) {
-            layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share());
+            layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
+                                    outputState.layerFilter.layerStack);
         }
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index aaf0f06..9e0e7b5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3220,16 +3220,19 @@
     // are passed. This happens to work with the current implementation, but
     // would not survive certain calls like Fence::merge() which would return a
     // new instance.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_))
-            .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_, _))
+            .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                     ui::LayerStack) {
                 EXPECT_EQ(FenceResult(layer1Fence), futureFenceResult.get());
             });
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_))
-            .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_, _))
+            .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                     ui::LayerStack) {
                 EXPECT_EQ(FenceResult(layer2Fence), futureFenceResult.get());
             });
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_))
-            .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
+            .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                     ui::LayerStack) {
                 EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
             });
 
@@ -3285,16 +3288,19 @@
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
     // Each released layer should be given the presentFence.
-    EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_, _))
+            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                      ui::LayerStack) {
                 EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
             });
-    EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_, _))
+            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                      ui::LayerStack) {
                 EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
             });
-    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
+            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                      ui::LayerStack) {
                 EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
             });
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 01db0cd..20f4de1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -535,8 +535,8 @@
 }
 
 void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
-    using fps_approx_ops::operator==;
-    if (mRequestedRefreshRate == 0_Hz) {
+    using fps_approx_ops::operator<=;
+    if (mRequestedRefreshRate <= 0_Hz) {
         return;
     }
 
@@ -547,7 +547,12 @@
     }
 
     unsigned divisor = static_cast<unsigned>(
-            std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+            std::floor(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+    if (divisor == 0) {
+        mAdjustedRefreshRate = 0_Hz;
+        return;
+    }
+
     mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index f28bfd4..f7049b9 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -329,14 +329,6 @@
     }
 }
 
-void AidlComposer::resetCommands(Display display) {
-    mMutex.lock_shared();
-    if (auto writer = getWriter(display)) {
-        writer->get().reset();
-    }
-    mMutex.unlock_shared();
-}
-
 Error AidlComposer::executeCommands(Display display) {
     mMutex.lock_shared();
     auto error = execute(display);
@@ -1054,9 +1046,8 @@
         return Error::BAD_DISPLAY;
     }
 
-    const auto& commands = writer->get().getPendingCommands();
+    auto commands = writer->get().takePendingCommands();
     if (commands.empty()) {
-        writer->get().reset();
         return Error::NONE;
     }
 
@@ -1088,8 +1079,6 @@
         }
     }
 
-    writer->get().reset();
-
     return error;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 8313c09..ce05b38 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -72,10 +72,6 @@
 
     void registerCallback(HWC2::ComposerCallback& callback) override;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    void resetCommands(Display) override;
-
     // Explicitly flush all pending commands in the command buffer.
     Error executeCommands(Display) override;
 
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c65c572..cf67795 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -110,10 +110,6 @@
 
     virtual void registerCallback(HWC2::ComposerCallback& callback) = 0;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    virtual void resetCommands(Display) = 0;
-
     // Explicitly flush all pending commands in the command buffer.
     virtual Error executeCommands(Display) = 0;
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 23de4fa..e0f6c45 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -293,10 +293,6 @@
     }
 }
 
-void HidlComposer::resetCommands(Display) {
-    mWriter.reset();
-}
-
 Error HidlComposer::executeCommands(Display) {
     return execute();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d04652b..0521acf 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -174,10 +174,6 @@
 
     void registerCallback(HWC2::ComposerCallback& callback) override;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    void resetCommands(Display) override;
-
     // Explicitly flush all pending commands in the command buffer.
     Error executeCommands(Display) override;
 
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index 6af352c..cfa2b03 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -23,6 +23,7 @@
 namespace android::surfaceflinger {
 
 std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+std::atomic<uint32_t> LayerCreationArgs::sInternalSequence{1};
 
 uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) {
     return id | INTERNAL_LAYER_PREFIX;
@@ -48,13 +49,11 @@
                 metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid)));
     }
 
-    if (id) {
+    if (internalLayer) {
+        sequence = getInternalLayerId(sInternalSequence++);
+    } else if (id) {
         sequence = *id;
-        if (internalLayer) {
-            sequence = getInternalLayerId(*id);
-        } else {
-            sSequence = *id + 1;
-        }
+        sSequence = *id + 1;
     } else {
         sequence = sSequence++;
         if (sequence >= INTERNAL_LAYER_PREFIX) {
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 3a0fc6d..c26edb5 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -36,6 +36,7 @@
 
 struct LayerCreationArgs {
     static std::atomic<uint32_t> sSequence;
+    static std::atomic<uint32_t> sInternalSequence;
     static uint32_t getInternalLayerId(uint32_t id);
     static LayerCreationArgs fromOtherArgs(const LayerCreationArgs& other);
 
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 33d9dbe..6cacfb5 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -41,7 +41,7 @@
             LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
                              it->second.owner.getDebugString().c_str());
         }
-
+        mAddedLayers.push_back(newLayer.get());
         layer.parentId = linkLayer(layer.parentId, layer.id);
         layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
         if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
@@ -258,23 +258,19 @@
 }
 
 void LayerLifecycleManager::commitChanges() {
-    for (auto& layer : mLayers) {
-        if (layer->changes.test(RequestedLayerState::Changes::Created)) {
-            for (auto listener : mListeners) {
-                listener->onLayerAdded(*layer);
-            }
+    for (auto layer : mAddedLayers) {
+        for (auto& listener : mListeners) {
+            listener->onLayerAdded(*layer);
         }
+    }
+    mAddedLayers.clear();
+
+    for (auto& layer : mLayers) {
         layer->clearChanges();
     }
 
     for (auto& destroyedLayer : mDestroyedLayers) {
-        if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) {
-            for (auto listener : mListeners) {
-                listener->onLayerAdded(*destroyedLayer);
-            }
-        }
-
-        for (auto listener : mListeners) {
+        for (auto& listener : mListeners) {
             listener->onLayerDestroyed(*destroyedLayer);
         }
     }
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index f258678..f0d2c22 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -108,6 +108,9 @@
     std::vector<std::unique_ptr<RequestedLayerState>> mLayers;
     // Layers pending destruction. Layers will be destroyed once changes are committed.
     std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers;
+    // Keeps track of all the layers that were added in order. Changes will be cleared once
+    // committed.
+    std::vector<RequestedLayerState*> mAddedLayers;
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 1e931a7..a992584 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,7 +27,6 @@
 LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
                              const LayerHierarchy::TraversalPath& path)
       : path(path) {
-    static uint32_t sUniqueSequenceId = 0;
     // Provide a unique id for all snapshots.
     // A front end layer can generate multiple snapshots if its mirrored.
     // Additionally, if the layer is not reachable, we may choose to destroy
@@ -35,7 +34,12 @@
     // change. The consumer shouldn't tie any lifetimes to this unique id but
     // register a LayerLifecycleManager::ILifecycleListener or get a list of
     // destroyed layers from LayerLifecycleManager.
-    uniqueSequence = sUniqueSequenceId++;
+    if (path.isClone()) {
+        uniqueSequence =
+                LayerCreationArgs::getInternalLayerId(LayerCreationArgs::sInternalSequence++);
+    } else {
+        uniqueSequence = state.id;
+    }
     sequence = static_cast<int32_t>(state.id);
     name = state.name;
     textureName = state.textureName;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index b397b82..23bb54c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -130,7 +130,8 @@
 void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
     const uint32_t oldFlags = flags;
     const half oldAlpha = color.a;
-    const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
+    const bool hadBuffer = externalTexture != nullptr;
+    const bool hadSideStream = sidebandStream != nullptr;
     const layer_state_t& clientState = resolvedComposerState.state;
     const bool hadBlur = hasBlur();
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
@@ -146,23 +147,32 @@
             changes |= RequestedLayerState::Changes::Geometry;
         }
     }
-    if (clientState.what &
-        (layer_state_t::eBufferChanged | layer_state_t::eSidebandStreamChanged)) {
-        const bool hasBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
-        if (hadBufferOrSideStream != hasBufferOrSideStream) {
+    if (clientState.what & layer_state_t::eBufferChanged) {
+        externalTexture = resolvedComposerState.externalTexture;
+        barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+        barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+        // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
+
+        const bool hasBuffer = externalTexture != nullptr;
+        if (hasBuffer || hasBuffer != hadBuffer) {
+            changes |= RequestedLayerState::Changes::Buffer;
+        }
+
+        if (hasBuffer != hadBuffer) {
             changes |= RequestedLayerState::Changes::Geometry |
                     RequestedLayerState::Changes::VisibleRegion |
                     RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
         }
     }
-    if (clientState.what & layer_state_t::eBufferChanged) {
-        barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
-        barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
-        // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
-        changes |= RequestedLayerState::Changes::Buffer;
-    }
+
     if (clientState.what & layer_state_t::eSidebandStreamChanged) {
         changes |= RequestedLayerState::Changes::SidebandStream;
+        const bool hasSideStream = sidebandStream != nullptr;
+        if (hasSideStream != hadSideStream) {
+            changes |= RequestedLayerState::Changes::Geometry |
+                    RequestedLayerState::Changes::VisibleRegion |
+                    RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+        }
     }
     if (what & (layer_state_t::eAlphaChanged)) {
         if (oldAlpha == 0 || color.a == 0) {
@@ -236,10 +246,6 @@
         // TODO(b/238781169) handle callbacks
     }
 
-    if (clientState.what & layer_state_t::eBufferChanged) {
-        externalTexture = resolvedComposerState.externalTexture;
-    }
-
     if (clientState.what & layer_state_t::ePositionChanged) {
         requestedTransform.set(x, y);
     }
@@ -465,6 +471,10 @@
     return hasFrameUpdate() && sidebandStream.get();
 }
 
+bool RequestedLayerState::willReleaseBufferOnLatch() const {
+    return changes.test(Changes::Buffer) && !externalTexture;
+}
+
 void RequestedLayerState::clearChanges() {
     what = 0;
     changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index f15f023..0ef50bc 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -79,6 +79,7 @@
     bool hasFrameUpdate() const;
     bool hasReadyFrame() const;
     bool hasSidebandStreamFrame() const;
+    bool willReleaseBufferOnLatch() const;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index a209cad..9cbe0bb 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -21,6 +21,7 @@
 
 #include <cutils/trace.h>
 #include <utils/Log.h>
+#include <utils/Trace.h>
 
 #include "TransactionHandler.h"
 
@@ -73,12 +74,13 @@
 
 void TransactionHandler::applyUnsignaledBufferTransaction(
         std::vector<TransactionState>& transactions, TransactionFlushState& flushState) {
-    // only apply an unsignaled buffer transaction if it's the first one
-    if (!transactions.empty()) {
+    if (!flushState.queueWithUnsignaledBuffer) {
         return;
     }
 
-    if (!flushState.queueWithUnsignaledBuffer) {
+    // only apply an unsignaled buffer transaction if it's the first one
+    if (!transactions.empty()) {
+        ATRACE_NAME("fence unsignaled");
         return;
     }
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a538c6d..9e40d7f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2442,16 +2442,7 @@
         info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
     }
 
-    // For compatibility reasons we let layers which can receive input
-    // receive input before they have actually submitted a buffer. Because
-    // of this we use canReceiveInput instead of isVisible to check the
-    // policy-visibility, ignoring the buffer state. However for layers with
-    // hasInputInfo()==false we can use the real visibility state.
-    // We are just using these layers for occlusion detection in
-    // InputDispatcher, and obviously if they aren't visible they can't occlude
-    // anything.
-    const bool visible = hasInputInfo() ? canReceiveInput() : isVisible();
-    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());
 
     info.alpha = getAlpha();
     fillTouchOcclusionMode(info);
@@ -2804,7 +2795,8 @@
                               currentMaxAcquiredBufferCount);
 }
 
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+                             ui::LayerStack layerStack) {
     // If we are displayed on multiple displays in a single composition cycle then we would
     // need to do careful tracking to enable the use of the mLastClientCompositionFence.
     //  For example we can only use it if all the displays are client comp, and we need
@@ -2834,8 +2826,7 @@
     // transaction doesn't need a previous release fence.
     sp<CallbackHandle> ch;
     for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
+        if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
             ch = handle;
             break;
         }
@@ -2851,6 +2842,7 @@
         ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
         ch->name = mName;
     }
+    mPreviouslyPresentedLayerStacks.push_back(layerStack);
 }
 
 void Layer::onSurfaceFrameCreated(
@@ -2889,8 +2881,7 @@
     }
 
     for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
+        if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
             handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
             break;
         }
@@ -3027,14 +3018,22 @@
     return true;
 }
 
+void Layer::resetDrawingStateBufferInfo() {
+    mDrawingState.producerId = 0;
+    mDrawingState.frameNumber = 0;
+    mDrawingState.releaseBufferListener = nullptr;
+    mDrawingState.buffer = nullptr;
+    mDrawingState.acquireFence = sp<Fence>::make(-1);
+    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+    mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+    mDrawingState.releaseBufferEndpoint = nullptr;
+}
+
 bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
                       const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
                       bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
                       const FrameTimelineInfo& info) {
     ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
-    if (!buffer) {
-        return false;
-    }
 
     const bool frameNumberChanged =
             bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
@@ -3066,12 +3065,24 @@
                                       mLastClientCompositionFence);
             mLastClientCompositionFence = nullptr;
         }
-    } else {
+    } else if (buffer) {
         // if we are latching a buffer for the first time then clear the mLastLatchTime since
         // we don't want to incorrectly classify a frame if we miss the desired present time.
         updateLastLatchTime(0);
     }
 
+    mDrawingState.desiredPresentTime = desiredPresentTime;
+    mDrawingState.isAutoTimestamp = isAutoTimestamp;
+    mDrawingState.latchedVsyncId = info.vsyncId;
+    mDrawingState.modified = true;
+    if (!buffer) {
+        resetDrawingStateBufferInfo();
+        setTransactionFlags(eTransactionNeeded);
+        mDrawingState.bufferSurfaceFrameTX = nullptr;
+        setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+        return true;
+    }
+
     mDrawingState.producerId = bufferData.producerId;
     mDrawingState.barrierProducerId =
             std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
@@ -3082,7 +3093,6 @@
     // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
     mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
     mDrawingState.buffer = std::move(buffer);
-    mDrawingState.clientCacheId = bufferData.cachedBuffer;
     mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
             ? bufferData.acquireFence
             : Fence::NO_FENCE;
@@ -3095,15 +3105,11 @@
     } else {
         mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
     }
-    mDrawingState.latchedVsyncId = info.vsyncId;
-    mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
                                       mOwnerUid, postTime, getGameMode());
-    mDrawingState.desiredPresentTime = desiredPresentTime;
-    mDrawingState.isAutoTimestamp = isAutoTimestamp;
 
     if (mFlinger->mLegacyFrontEndEnabled) {
         recordLayerHistoryBufferUpdate(getLayerProps());
@@ -3351,7 +3357,7 @@
     const State& s(getDrawingState());
 
     if (!s.buffer) {
-        if (bgColorOnly) {
+        if (bgColorOnly || mBufferInfo.mBuffer) {
             for (auto& handle : mDrawingState.callbackHandles) {
                 handle->latchTime = latchTime;
             }
@@ -3398,12 +3404,19 @@
 }
 
 void Layer::gatherBufferInfo() {
-    if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
+    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
+    mPreviousReleaseBufferEndpoint = mBufferInfo.mReleaseBufferEndpoint;
+    if (!mDrawingState.buffer) {
+        mBufferInfo = {};
+        return;
+    }
+
+    if ((!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer))) {
         decrementPendingBufferCount();
     }
 
-    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
     mBufferInfo.mBuffer = mDrawingState.buffer;
+    mBufferInfo.mReleaseBufferEndpoint = mDrawingState.releaseBufferEndpoint;
     mBufferInfo.mFence = mDrawingState.acquireFence;
     mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
     mBufferInfo.mPixelFormat =
@@ -3933,6 +3946,10 @@
     mBufferInfo.mFrameLatencyNeeded = false;
 }
 
+bool Layer::willReleaseBufferOnLatch() const {
+    return !mDrawingState.buffer && mBufferInfo.mBuffer;
+}
+
 bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
     const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
     return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
@@ -3956,9 +3973,6 @@
         return false;
     }
     updateTexImage(latchTime, bgColorOnly);
-    if (mDrawingState.buffer == nullptr) {
-        return false;
-    }
 
     // Capture the old state of the layer for comparisons later
     BufferInfo oldBufferInfo = mBufferInfo;
@@ -3967,6 +3981,18 @@
     mCurrentFrameNumber = mDrawingState.frameNumber;
     gatherBufferInfo();
 
+    if (mBufferInfo.mBuffer) {
+        // We latched a buffer that will be presented soon. Clear the previously presented layer
+        // stack list.
+        mPreviouslyPresentedLayerStacks.clear();
+    }
+
+    if (mDrawingState.buffer == nullptr) {
+        const bool bufferReleased = oldBufferInfo.mBuffer != nullptr;
+        recomputeVisibleRegions = bufferReleased;
+        return bufferReleased;
+    }
+
     if (oldBufferInfo.mBuffer == nullptr) {
         // the first time we receive a buffer, we need to trigger a
         // geometry invalidation.
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index acdd01d..b37fa15 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -153,7 +153,6 @@
         bool transformToDisplayInverse;
         Region transparentRegionHint;
         std::shared_ptr<renderengine::ExternalTexture> buffer;
-        client_cache_t clientCacheId;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
         HdrMetadata hdrMetadata;
@@ -372,6 +371,21 @@
     bool canReceiveInput() const;
 
     /*
+     * Whether or not the layer should be considered visible for input calculations.
+     */
+    virtual bool isVisibleForInput() const {
+        // For compatibility reasons we let layers which can receive input
+        // receive input before they have actually submitted a buffer. Because
+        // of this we use canReceiveInput instead of isVisible to check the
+        // policy-visibility, ignoring the buffer state. However for layers with
+        // hasInputInfo()==false we can use the real visibility state.
+        // We are just using these layers for occlusion detection in
+        // InputDispatcher, and obviously if they aren't visible they can't occlude
+        // anything.
+        return hasInputInfo() ? canReceiveInput() : isVisible();
+    }
+
+    /*
      * isProtected - true if the layer may contain protected contents in the
      * GRALLOC_USAGE_PROTECTED sense.
      */
@@ -440,6 +454,12 @@
                          bool bgColorOnly);
 
     /*
+     * Returns true if the currently presented buffer will be released when this layer state
+     * is latched. This will return false if there is no buffer currently presented.
+     */
+    bool willReleaseBufferOnLatch() const;
+
+    /*
      * Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
      * This is used if the buffer is just latched and releases to free up the buffer
      * and will not be shown on screen.
@@ -521,6 +541,7 @@
 
         std::shared_ptr<renderengine::ExternalTexture> mBuffer;
         uint64_t mFrameNumber;
+        sp<IBinder> mReleaseBufferEndpoint;
 
         bool mFrameLatencyNeeded{false};
         float mDesiredHdrSdrRatio = 1.f;
@@ -532,7 +553,7 @@
     const compositionengine::LayerFECompositionState* getCompositionState() const;
     bool fenceHasSignaled() const;
     void onPreComposition(nsecs_t refreshStartTime);
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
+    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
 
     void setWasClientComposed(const sp<Fence>& fence) {
         mLastClientCompositionFence = fence;
@@ -885,7 +906,10 @@
     void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
         mTransformHint = transformHint;
     }
-
+    // Keeps track of the previously presented layer stacks. This is used to get
+    // the release fences from the correct displays when we release the last buffer
+    // from the layer.
+    std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
     // Exposed so SurfaceFlinger can assert that it's held
     const sp<SurfaceFlinger> mFlinger;
 
@@ -1162,6 +1186,7 @@
     half4 mBorderColor;
 
     void setTransformHintLegacy(ui::Transform::RotationFlags);
+    void resetDrawingStateBufferInfo();
 
     const uint32_t mTextureName;
 
@@ -1172,6 +1197,7 @@
     std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
 
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
+    sp<IBinder> mPreviousReleaseBufferEndpoint;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
     uint64_t mPreviousBarrierFrameNumber = 0;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b9c8b78..e713263 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -325,8 +325,9 @@
     caster.shadow = state;
 }
 
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
-    mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult));
+void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+                               ui::LayerStack layerStack) {
+    mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
 }
 
 CompositionResult&& LayerFE::stealCompositionResult() {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index c23bd31..d584fb7 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -29,7 +29,7 @@
     // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
     // and remove this field.
     nsecs_t refreshStartTime = 0;
-    std::vector<ftl::SharedFuture<FenceResult>> releaseFences;
+    std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
     sp<Fence> lastClientCompositionFence = nullptr;
 };
 
@@ -40,7 +40,7 @@
     // compositionengine::LayerFE overrides
     const compositionengine::LayerFECompositionState* getCompositionState() const override;
     bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
     const char* getDebugName() const override;
     int32_t getSequence() const override;
     bool hasRoundedCorners() const override;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 5d92485..3472d20 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -392,7 +392,11 @@
 
     layerInfo->set_id(snapshot.uniqueSequence);
     layerInfo->set_original_id(snapshot.sequence);
-    layerInfo->set_name(requestedState.name);
+    if (!snapshot.path.isClone()) {
+        layerInfo->set_name(requestedState.name);
+    } else {
+        layerInfo->set_name(requestedState.name + "(Mirror)");
+    }
     layerInfo->set_type("Layer");
 
     LayerProtoHelper::writeToProto(requestedState.transparentRegion,
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index ed65bc6..67e1b9c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <ftl/algorithm.h>
 #include <ftl/small_map.h>
 #include <utils/Timers.h>
 
@@ -82,12 +83,18 @@
         flushTime();
 
         TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
-        const auto zero = std::chrono::milliseconds::zero();
 
         // Sum the times for modes that map to the same name, e.g. "60 Hz".
         for (const auto& [fps, time] : mFpsTotalTimes) {
             const auto string = to_string(fps);
-            const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+            const auto total = std::as_const(totalTimes)
+                                       .get(string)
+                                       .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+                                           using namespace std::chrono_literals;
+                                           return 0ms;
+                                       }))
+                                       .value();
+
             totalTimes.emplace_or_replace(string, total.get() + time);
         }
 
@@ -114,15 +121,18 @@
         mPreviousRecordedTime = currentTime;
 
         const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
-        const auto zero = std::chrono::milliseconds::zero();
-
         uint32_t fps = 0;
 
         if (mCurrentPowerMode == PowerMode::ON) {
             // Normal power mode is counted under different config modes.
             const auto total = std::as_const(mFpsTotalTimes)
                                        .get(mCurrentRefreshRate)
-                                       .value_or(std::cref(zero));
+                                       .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+                                           using namespace std::chrono_literals;
+                                           return 0ms;
+                                       }))
+                                       .value();
+
             mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
 
             fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8ddcfa1..63a0173 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -50,8 +50,9 @@
 #include "FrontEnd/LayerHandle.h"
 #include "OneShotTimer.h"
 #include "SurfaceFlingerProperties.h"
-#include "VSyncPredictor.h"
-#include "VSyncReactor.h"
+#include "VSyncTracker.h"
+#include "VsyncController.h"
+#include "VsyncSchedule.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -119,18 +120,17 @@
 
 void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
                                         RefreshRateSelectorPtr selectorPtr,
-                                        std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+                                        VsyncSchedulePtr schedulePtr) {
     demotePacesetterDisplay();
 
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
         std::scoped_lock lock(mDisplayLock);
-        mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
-        mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
+        mDisplays.emplace_or_replace(displayId, std::move(selectorPtr), std::move(schedulePtr));
 
         pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
     }
-    applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
@@ -139,17 +139,16 @@
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
         std::scoped_lock lock(mDisplayLock);
-        mRefreshRateSelectors.erase(displayId);
-        mVsyncSchedules.erase(displayId);
+        mDisplays.erase(displayId);
 
         // Do not allow removing the final display. Code in the scheduler expects
         // there to be at least one display. (This may be relaxed in the future with
         // headless virtual display.)
-        LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
+        LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
 
         pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
     }
-    applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 void Scheduler::run() {
@@ -199,20 +198,27 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps;
-        const nsecs_t currentPeriod =
-                getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs();
+        const auto [refreshRate, period] = [this] {
+            std::scoped_lock lock(mDisplayLock);
+            const auto pacesetterOpt = pacesetterDisplayLocked();
+            LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+            const Display& pacesetter = *pacesetterOpt;
+            return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
+                                  pacesetter.schedulePtr->period());
+        }();
+
+        const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
 
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
-            return currentPeriod;
+            return currentPeriod.ns();
         }
 
         const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
         if (divisor <= 1) {
-            return currentPeriod;
+            return currentPeriod.ns();
         }
-        return currentPeriod * divisor;
+        return currentPeriod.ns() * divisor;
     };
 }
 
@@ -413,23 +419,24 @@
     std::scoped_lock lock(mDisplayLock);
     ftl::FakeGuard guard(kMainThreadContext);
 
-    for (const auto& [id, _] : mRefreshRateSelectors) {
+    for (const auto& [id, _] : mDisplays) {
         resyncToHardwareVsyncLocked(id, allowToEnable);
     }
 }
 
 void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
                                             std::optional<Fps> refreshRate) {
-    auto schedule = getVsyncScheduleLocked(id);
-    if (schedule->isHardwareVsyncAllowed(allowToEnable)) {
+    const auto displayOpt = mDisplays.get(id);
+    LOG_ALWAYS_FATAL_IF(!displayOpt);
+    const Display& display = *displayOpt;
+
+    if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
         if (!refreshRate) {
-            auto selectorPtr = mRefreshRateSelectors.get(id);
-            LOG_ALWAYS_FATAL_IF(!selectorPtr);
-            refreshRate = selectorPtr->get()->getActiveMode().modePtr->getFps();
+            refreshRate = display.selectorPtr->getActiveMode().modePtr->getFps();
         }
         if (refreshRate->isValid()) {
-            schedule->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
-                                            false /* force */);
+            display.schedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
+                                                       false /* force */);
         }
     }
 }
@@ -438,9 +445,10 @@
     std::scoped_lock lock(mDisplayLock);
     ftl::FakeGuard guard(kMainThreadContext);
 
-    auto selectorPtr = mRefreshRateSelectors.get(id);
-    LOG_ALWAYS_FATAL_IF(!selectorPtr);
-    const auto mode = selectorPtr->get()->getActiveMode();
+    const auto displayOpt = mDisplays.get(id);
+    LOG_ALWAYS_FATAL_IF(!displayOpt);
+    const Display& display = *displayOpt;
+    const auto mode = display.selectorPtr->getActiveMode();
 
     using fps_approx_ops::operator!=;
     LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
@@ -451,7 +459,7 @@
     ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
           to_string(mode.modePtr->getFps()).c_str());
 
-    getVsyncScheduleLocked(id)->getTracker().setRenderRate(renderFrameRate);
+    display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
 }
 
 void Scheduler::resync() {
@@ -558,22 +566,24 @@
     mLayerHistory.clear();
 }
 
-std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncSchedule(
-        std::optional<PhysicalDisplayId> idOpt) const {
+auto Scheduler::getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt) const
+        -> ConstVsyncSchedulePtr {
     std::scoped_lock lock(mDisplayLock);
     return getVsyncScheduleLocked(idOpt);
 }
 
-std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncScheduleLocked(
-        std::optional<PhysicalDisplayId> idOpt) const {
+auto Scheduler::getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt) const
+        -> ConstVsyncSchedulePtr {
     ftl::FakeGuard guard(kMainThreadContext);
+
     if (!idOpt) {
         LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
         idOpt = mPacesetterDisplayId;
     }
-    auto scheduleOpt = mVsyncSchedules.get(*idOpt);
-    LOG_ALWAYS_FATAL_IF(!scheduleOpt);
-    return std::const_pointer_cast<const VsyncSchedule>(scheduleOpt->get());
+
+    const auto displayOpt = mDisplays.get(*idOpt);
+    LOG_ALWAYS_FATAL_IF(!displayOpt);
+    return displayOpt->get().schedulePtr;
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -597,9 +607,9 @@
         // need to update the VsyncController model anyway.
         std::scoped_lock lock(mDisplayLock);
         ftl::FakeGuard guard(kMainThreadContext);
-        constexpr bool disallow = false;
-        for (auto& [_, schedule] : mVsyncSchedules) {
-            schedule->disableHardwareVsync(mSchedulerCallback, disallow);
+        for (const auto& [_, display] : mDisplays) {
+            constexpr bool kDisallow = false;
+            display.schedulePtr->disableHardwareVsync(mSchedulerCallback, kDisallow);
         }
     }
 
@@ -664,12 +674,12 @@
                             to_string(*mPacesetterDisplayId).c_str());
         getVsyncScheduleLocked()->dump(out);
     }
-    for (auto& [id, vsyncSchedule] : mVsyncSchedules) {
+    for (auto& [id, display] : mDisplays) {
         if (id == mPacesetterDisplayId) {
             continue;
         }
         base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
-        vsyncSchedule->dump(out);
+        display.schedulePtr->dump(out);
     }
 }
 
@@ -693,49 +703,39 @@
         pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
     }
 
-    applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
         std::optional<PhysicalDisplayId> pacesetterIdOpt) {
     // TODO(b/241286431): Choose the pacesetter display.
-    const auto oldPacesetterDisplayIdOpt = mPacesetterDisplayId;
-    mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+    mPacesetterDisplayId = pacesetterIdOpt.value_or(mDisplays.begin()->first);
     ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
 
-    auto newVsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId);
-    if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) {
-        pacesetterPtr->setIdleTimerCallbacks(
+    std::shared_ptr<VsyncSchedule> newVsyncSchedulePtr;
+    if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
+        const Display& pacesetter = *pacesetterOpt;
+
+        pacesetter.selectorPtr->setIdleTimerCallbacks(
                 {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
                               .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
                  .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
                             .onExpired =
                                     [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
 
-        pacesetterPtr->startIdleTimer();
+        pacesetter.selectorPtr->startIdleTimer();
 
-        // Track the new period, which may have changed due to switching to a
-        // new pacesetter or due to a hotplug event. In the former case, this
-        // is important so that VSYNC modulation does not get stuck in the
-        // initiated state if a transition started on the old pacesetter.
-        const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps();
-        newVsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
-                                                true /* force */);
+        newVsyncSchedulePtr = pacesetter.schedulePtr;
+
+        const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getFps();
+        newVsyncSchedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
+                                                   true /* force */);
     }
-    if (oldPacesetterDisplayIdOpt == mPacesetterDisplayId) {
-        return nullptr;
-    }
-    return newVsyncSchedule;
+    return newVsyncSchedulePtr;
 }
 
-void Scheduler::applyNewVsyncScheduleIfNonNull(
-        std::shared_ptr<VsyncSchedule> pacesetterSchedulePtr) {
-    if (!pacesetterSchedulePtr) {
-        // The pacesetter has not changed, so there is no new VsyncSchedule to
-        // apply.
-        return;
-    }
-    onNewVsyncSchedule(pacesetterSchedulePtr->getDispatch());
+void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+    onNewVsyncSchedule(vsyncSchedule->getDispatch());
     std::vector<android::EventThread*> threads;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -745,7 +745,7 @@
         }
     }
     for (auto* thread : threads) {
-        thread->onNewVsyncSchedule(pacesetterSchedulePtr);
+        thread->onNewVsyncSchedule(vsyncSchedule);
     }
 }
 
@@ -845,9 +845,10 @@
 
     const auto globalSignals = makeGlobalSignals();
 
-    for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
+    for (const auto& [id, display] : mDisplays) {
         auto rankedFrameRates =
-                selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
+                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
+                                                         globalSignals);
 
         for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
             const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
@@ -866,7 +867,7 @@
 
     // Find the first refresh rate common to all displays.
     while (maxScoreIt != refreshRateTallies.cend() &&
-           maxScoreIt->second.displayCount != mRefreshRateSelectors.size()) {
+           maxScoreIt->second.displayCount != mDisplays.size()) {
         ++maxScoreIt;
     }
 
@@ -875,8 +876,7 @@
         for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
             const auto [fps, tally] = *it;
 
-            if (tally.displayCount == mRefreshRateSelectors.size() &&
-                tally.score > maxScoreIt->second.score) {
+            if (tally.displayCount == mDisplays.size() && tally.score > maxScoreIt->second.score) {
                 maxScoreIt = it;
             }
         }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 720a1cb..43aab2d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -50,7 +50,6 @@
 #include "RefreshRateSelector.h"
 #include "Utils/Dumper.h"
 #include "VsyncModulator.h"
-#include "VsyncSchedule.h"
 
 namespace android::scheduler {
 
@@ -93,6 +92,8 @@
 
 using GlobalSignals = RefreshRateSelector::GlobalSignals;
 
+class VsyncSchedule;
+
 class Scheduler : android::impl::MessageQueue {
     using Impl = android::impl::MessageQueue;
 
@@ -108,6 +109,9 @@
 
     using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
 
+    using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>;
+    using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>;
+
     void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
             EXCLUDES(mDisplayLock);
     void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
@@ -236,12 +240,12 @@
     void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
             REQUIRES(kMainThreadContext);
 
-    std::shared_ptr<const VsyncSchedule> getVsyncSchedule(
-            std::optional<PhysicalDisplayId> idOpt = std::nullopt) const EXCLUDES(mDisplayLock);
-    std::shared_ptr<VsyncSchedule> getVsyncSchedule(
-            std::optional<PhysicalDisplayId> idOpt = std::nullopt) EXCLUDES(mDisplayLock) {
-        return std::const_pointer_cast<VsyncSchedule>(
-                static_cast<const Scheduler*>(this)->getVsyncSchedule(idOpt));
+    ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const
+            EXCLUDES(mDisplayLock);
+
+    VsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+            EXCLUDES(mDisplayLock) {
+        return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
     }
 
     // Returns true if a given vsync timestamp is considered valid vsync
@@ -329,20 +333,17 @@
     // MessageQueue and EventThread need to use the new pacesetter's
     // VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
     // or else we may deadlock with EventThread.
-    // Returns the new pacesetter's VsyncSchedule, or null if the pacesetter is
-    // unchanged.
     std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
             std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
             REQUIRES(kMainThreadContext, mDisplayLock);
-    void applyNewVsyncScheduleIfNonNull(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
+    void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
 
     // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
     // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
     void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
 
-    void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr,
-                                 std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext)
-            EXCLUDES(mDisplayLock);
+    void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     struct Policy;
 
@@ -421,16 +422,37 @@
     // must lock for writes but not reads. See also mPolicyLock for locking order.
     mutable std::mutex mDisplayLock;
 
-    display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
-            GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+    struct Display {
+        Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
+              : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
 
-    // TODO (b/266715559): Store in the same map as mRefreshRateSelectors.
-    display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules
-            GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+        // Effectively const except in move constructor.
+        RefreshRateSelectorPtr selectorPtr;
+        VsyncSchedulePtr schedulePtr;
+    };
+
+    using DisplayRef = std::reference_wrapper<Display>;
+    using ConstDisplayRef = std::reference_wrapper<const Display>;
+
+    display::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays GUARDED_BY(mDisplayLock)
+            GUARDED_BY(kMainThreadContext);
 
     ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
             GUARDED_BY(kMainThreadContext);
 
+    ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
+        return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
+                [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
+    }
+
+    ftl::Optional<ConstDisplayRef> pacesetterDisplayLocked() const REQUIRES(mDisplayLock) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return mPacesetterDisplayId.and_then([this](PhysicalDisplayId pacesetterId)
+                                                     REQUIRES(mDisplayLock, kMainThreadContext) {
+                                                         return mDisplays.get(pacesetterId);
+                                                     });
+    }
+
     RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
         std::scoped_lock lock(mDisplayLock);
         return pacesetterSelectorPtrLocked();
@@ -438,19 +460,17 @@
 
     RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
         ftl::FakeGuard guard(kMainThreadContext);
-        const RefreshRateSelectorPtr noPacesetter;
-        return mPacesetterDisplayId
-                .and_then([this](PhysicalDisplayId pacesetterId)
-                                  REQUIRES(mDisplayLock, kMainThreadContext) {
-                                      return mRefreshRateSelectors.get(pacesetterId);
-                                  })
-                .value_or(std::cref(noPacesetter));
+        return pacesetterDisplayLocked()
+                .transform([](const Display& display) { return display.selectorPtr; })
+                .or_else([] { return std::optional<RefreshRateSelectorPtr>(nullptr); })
+                .value();
     }
 
-    std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked(
-            std::optional<PhysicalDisplayId> idOpt = std::nullopt) const REQUIRES(mDisplayLock);
-    std::shared_ptr<VsyncSchedule> getVsyncScheduleLocked(
-            std::optional<PhysicalDisplayId> idOpt = std::nullopt) REQUIRES(mDisplayLock) {
+    ConstVsyncSchedulePtr getVsyncScheduleLocked(
+            std::optional<PhysicalDisplayId> = std::nullopt) const REQUIRES(mDisplayLock);
+
+    VsyncSchedulePtr getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+            REQUIRES(mDisplayLock) {
         return std::const_pointer_cast<VsyncSchedule>(
                 static_cast<const Scheduler*>(this)->getVsyncScheduleLocked(idOpt));
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8394ffb..c88bff5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2146,7 +2146,14 @@
     }));
 }
 
-auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod)
+bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
+    const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
+    return isThreeVsyncsAhead ||
+            getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
+            Fence::SIGNAL_TIME_PENDING;
+}
+
+auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
         -> const FenceTimePtr& {
     const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
     const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
@@ -2303,13 +2310,17 @@
                                           std::make_optional(layer->parentId), true));
                 mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
             }
-            if (!layer->hasReadyFrame()) continue;
+            const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
+            if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) continue;
 
             auto it = mLegacyLayers.find(layer->id);
             LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
                                 layer->getDebugString().c_str());
             const bool bgColorOnly =
                     !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+            if (willReleaseBufferOnLatch) {
+                mLayersWithBuffersRemoved.emplace(it->second);
+            }
             it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
             mLayersWithQueuedFrames.emplace(it->second);
         }
@@ -2610,21 +2621,14 @@
         refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
     }
 
-    const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule()->period();
-    const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
-
     const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
-    const bool threeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
 
-    // We should wait for the earliest present time if HWC doesn't support ExpectedPresentTime,
-    // and the next vsync is not already taken by the previous frame.
-    const bool waitForEarliestPresent =
-            !getHwComposer().getComposer()->isSupported(
-                    Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
-            (threeVsyncsAhead ||
-             mPreviousPresentFences[0].fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING);
+    if (!getHwComposer().getComposer()->isSupported(
+                Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+        wouldPresentEarly(frameTime, vsyncPeriod)) {
+        const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
+        const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
 
-    if (waitForEarliestPresent) {
         refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
     }
 
@@ -2644,10 +2648,10 @@
     for (auto [layer, layerFE] : layers) {
         CompositionResult compositionResult{layerFE->stealCompositionResult()};
         layer->onPreComposition(compositionResult.refreshStartTime);
-        for (auto releaseFence : compositionResult.releaseFences) {
+        for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
             Layer* clonedFrom = layer->getClonedFrom().get();
             auto owningLayer = clonedFrom ? clonedFrom : layer;
-            owningLayer->onLayerDisplayed(releaseFence);
+            owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
         }
         if (compositionResult.lastClientCompositionFence) {
             layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
@@ -2658,9 +2662,10 @@
 
     // Send a power hint hint after presentation is finished
     if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(mPreviousPresentFences[0]
-                                                                    .fenceTime->getSignalTime()),
-                                          TimePoint::now());
+        const nsecs_t pastPresentTime =
+                getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime();
+
+        mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(pastPresentTime), TimePoint::now());
         mPowerAdvisor->reportActualWorkDuration();
     }
 
@@ -2858,6 +2863,29 @@
     const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
                                             presentLatency.ns());
 
+    display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
+    {
+        if (!mLayersWithBuffersRemoved.empty() || mNumTrustedPresentationListeners > 0) {
+            Mutex::Autolock lock(mStateLock);
+            for (const auto& [token, display] : mDisplays) {
+                layerStackToDisplay.emplace_or_replace(display->getLayerStack(), display.get());
+            }
+        }
+    }
+
+    for (auto layer : mLayersWithBuffersRemoved) {
+        for (auto layerStack : layer->mPreviouslyPresentedLayerStacks) {
+            auto optDisplay = layerStackToDisplay.get(layerStack);
+            if (optDisplay && !optDisplay->get()->isVirtual()) {
+                auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
+                layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+                                        ui::INVALID_LAYER_STACK);
+            }
+        }
+        layer->releasePendingBuffer(presentTime.ns());
+    }
+    mLayersWithBuffersRemoved.clear();
+
     for (const auto& layer: mLayersWithQueuedFrames) {
         layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
                                  compositorTiming);
@@ -2984,14 +3012,6 @@
     }
 
     if (mNumTrustedPresentationListeners > 0) {
-        display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
-        {
-            Mutex::Autolock lock(mStateLock);
-            for (const auto& [token, display] : mDisplays) {
-                layerStackToDisplay.emplace_or_replace(display->getLayerStack(), display.get());
-            }
-        }
-
         // We avoid any reverse traversal upwards so this shouldn't be too expensive
         traverseLegacyLayers([&](Layer* layer) {
             if (!layer->hasTrustedPresentationListener()) {
@@ -3699,10 +3719,10 @@
 }
 
 void SurfaceFlinger::updateInputFlinger() {
-    ATRACE_CALL();
-    if (!mInputFlinger) {
+    if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
         return;
     }
+    ATRACE_CALL();
 
     std::vector<WindowInfo> windowInfos;
     std::vector<DisplayInfo> displayInfos;
@@ -3711,8 +3731,18 @@
         mUpdateInputInfo = false;
         updateWindowInfo = true;
         buildWindowInfos(windowInfos, displayInfos);
-    } else if (mInputWindowCommands.empty()) {
-        return;
+    }
+
+    std::unordered_set<int32_t> visibleWindowIds;
+    for (WindowInfo& windowInfo : windowInfos) {
+        if (!windowInfo.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
+            visibleWindowIds.insert(windowInfo.id);
+        }
+    }
+    bool visibleWindowsChanged = false;
+    if (visibleWindowIds != mVisibleWindowIds) {
+        visibleWindowsChanged = true;
+        mVisibleWindowIds = std::move(visibleWindowIds);
     }
 
     BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
@@ -3720,12 +3750,16 @@
                                                       displayInfos = std::move(displayInfos),
                                                       inputWindowCommands =
                                                               std::move(mInputWindowCommands),
-                                                      inputFlinger = mInputFlinger, this]() {
+                                                      inputFlinger = mInputFlinger, this,
+                                                      visibleWindowsChanged]() {
         ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
         if (updateWindowInfo) {
             mWindowInfosListenerInvoker
-                    ->windowInfosChanged(windowInfos, displayInfos,
-                                         inputWindowCommands.windowInfosReportedListeners);
+                    ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
+                                         std::move(
+                                                 inputWindowCommands.windowInfosReportedListeners),
+                                         /* forceImmediateCall= */ visibleWindowsChanged ||
+                                                 !inputWindowCommands.focusRequests.empty());
         } else {
             // If there are listeners but no changes to input windows, call the listeners
             // immediately.
@@ -4023,7 +4057,7 @@
             }
         }
 
-        if (layer->hasReadyFrame()) {
+        if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) {
             frameQueued = true;
             mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
         } else {
@@ -4054,6 +4088,9 @@
         Mutex::Autolock lock(mStateLock);
 
         for (const auto& layer : mLayersWithQueuedFrames) {
+            if (layer->willReleaseBufferOnLatch()) {
+                mLayersWithBuffersRemoved.emplace(layer);
+            }
             if (layer->latchBuffer(visibleRegions, latchTime)) {
                 mLayersPendingRefresh.push_back(layer);
                 newDataLatched = true;
@@ -4187,20 +4224,20 @@
         const TransactionHandler::TransactionFlushState& flushState) {
     using TransactionReadiness = TransactionHandler::TransactionReadiness;
     const auto& transaction = *flushState.transaction;
-    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
-                  transaction.frameTimelineInfo.vsyncId);
     TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
     // 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 (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
         desiredPresentTime < mExpectedPresentTime + 1s) {
-        ATRACE_NAME("not current");
+        ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
+                      desiredPresentTime, mExpectedPresentTime);
         return TransactionReadiness::NotReady;
     }
 
     if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
-        ATRACE_NAME("!isVsyncValid");
+        ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
+                      mExpectedPresentTime, transaction.originUid);
         return TransactionReadiness::NotReady;
     }
 
@@ -4208,7 +4245,8 @@
     // expected present time of this transaction.
     if (transaction.isAutoTimestamp &&
         frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
-        ATRACE_NAME("frameIsEarly");
+        ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
+                      transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
         return TransactionReadiness::NotReady;
     }
     return TransactionReadiness::Ready;
@@ -4238,7 +4276,9 @@
                                                  s.bufferData->acquireFence);
                 // Delete the entire state at this point and not just release the buffer because
                 // everything associated with the Layer in this Transaction is now out of date.
-                ATRACE_NAME("DeleteStaleBuffer");
+                ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+                              layer->getDebugName(), layer->getDrawingState().barrierProducerId,
+                              s.bufferData->producerId);
                 return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
             }
 
@@ -4248,7 +4288,10 @@
                         ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
                           s.bufferData->barrierFrameNumber));
                 if (!willApplyBarrierFrame) {
-                    ATRACE_NAME("NotReadyBarrier");
+                    ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+                                  layer->getDebugName(),
+                                  layer->getDrawingState().barrierFrameNumber,
+                                  s.bufferData->barrierFrameNumber);
                     ready = TransactionReadiness::NotReadyBarrier;
                     return TraverseBuffersReturnValues::STOP_TRAVERSAL;
                 }
@@ -4260,7 +4303,7 @@
         const bool hasPendingBuffer =
                 flushState.bufferLayersReadyToPresent.contains(s.surface.get());
         if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
-            ATRACE_NAME("hasPendingBuffer");
+            ATRACE_FORMAT("hasPendingBuffer %s", layer->getDebugName());
             ready = TransactionReadiness::NotReady;
             return TraverseBuffersReturnValues::STOP_TRAVERSAL;
         }
@@ -4277,9 +4320,9 @@
             const bool allowLatchUnsignaled =
                     shouldLatchUnsignaled(layer, s, transaction.states.size(),
                                           flushState.firstTransaction);
-            ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
-                          allowLatchUnsignaled ? "true" : "false");
             if (allowLatchUnsignaled) {
+                ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+                              layer->getDebugName());
                 ready = TransactionReadiness::NotReadyUnsignaled;
             } else {
                 ready = TransactionReadiness::NotReady;
@@ -4292,12 +4335,12 @@
                                                        "Buffer processing hung up due to stuck "
                                                        "fence. Indicates GPU hang");
                 }
+                ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
                 return TraverseBuffersReturnValues::STOP_TRAVERSAL;
             }
         }
         return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
     });
-    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
     return ready;
 }
 
@@ -5047,7 +5090,8 @@
     }
 
     if (layer->setTransactionCompletedListeners(callbackHandles,
-                                                layer->willPresentCurrentTransaction())) {
+                                                layer->willPresentCurrentTransaction() ||
+                                                        layer->willReleaseBufferOnLatch())) {
         flags |= eTraversalNeeded;
     }
 
@@ -5161,8 +5205,9 @@
     }
 
     const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
-    bool willPresentCurrentTransaction =
-            requestedLayerState && requestedLayerState->hasReadyFrame();
+    bool willPresentCurrentTransaction = requestedLayerState &&
+            (requestedLayerState->hasReadyFrame() ||
+             requestedLayerState->willReleaseBufferOnLatch());
     if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
         flags |= eTraversalNeeded;
 
@@ -5825,8 +5870,8 @@
         return layersProto;
     }
 
-    return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, {},
-                                           traceFlags)
+    return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
+                                           mLegacyLayers, traceFlags)
             .generate(mLayerHierarchyBuilder.getHierarchy());
 }
 
@@ -7358,12 +7403,14 @@
                                                 : ftl::yield(present()).share();
 
     for (auto& [layer, layerFE] : layers) {
-        layer->onLayerDisplayed(
-                ftl::Future(presentFuture)
-                        .then([layerFE = std::move(layerFE)](FenceResult) {
-                            return layerFE->stealCompositionResult().releaseFences.back().get();
-                        })
-                        .share());
+        layer->onLayerDisplayed(ftl::Future(presentFuture)
+                                        .then([layerFE = std::move(layerFE)](FenceResult) {
+                                            return layerFE->stealCompositionResult()
+                                                    .releaseFences.back()
+                                                    .first.get();
+                                        })
+                                        .share(),
+                                ui::INVALID_LAYER_STACK);
     }
 
     return presentFuture;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5783c8d..cd7659b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -28,6 +28,7 @@
 #include <android/gui/ISurfaceComposerClient.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
+#include <ftl/algorithm.h>
 #include <ftl/future.h>
 #include <ftl/non_null.h>
 #include <gui/BufferQueue.h>
@@ -499,7 +500,7 @@
 
     // Implements ISurfaceComposer
     sp<IBinder> createDisplay(const String8& displayName, bool secure,
-                              float requestedRefreshRate = 0);
+                              float requestedRefreshRate = 0.0f);
     void destroyDisplay(const sp<IBinder>& displayToken);
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) {
         Mutex::Autolock lock(mStateLock);
@@ -854,8 +855,9 @@
     }
 
     sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
-        const sp<DisplayDevice> nullDisplay;
-        return mDisplays.get(displayToken).value_or(std::cref(nullDisplay));
+        return mDisplays.get(displayToken)
+                .or_else(ftl::static_ref<sp<DisplayDevice>>([] { return nullptr; }))
+                .value();
     }
 
     sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const
@@ -996,7 +998,9 @@
 
     using FenceTimePtr = std::shared_ptr<FenceTime>;
 
-    const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period)
+    bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
+
+    const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
             REQUIRES(kMainThreadContext);
 
     // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
@@ -1011,10 +1015,10 @@
      */
     sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
             REQUIRES(mStateLock) {
-        const sp<display::DisplayToken> nullToken;
         return mPhysicalDisplays.get(displayId)
                 .transform([](const display::PhysicalDisplay& display) { return display.token(); })
-                .value_or(std::cref(nullToken));
+                .or_else([] { return std::optional<sp<display::DisplayToken>>(nullptr); })
+                .value();
     }
 
     std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
@@ -1191,6 +1195,7 @@
     // Tracks layers that have pending frames which are candidates for being
     // latched.
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
+    std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
 
@@ -1424,6 +1429,9 @@
     TransactionHandler mTransactionHandler;
     display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
     bool mFrontEndDisplayInfosChanged = false;
+
+    // WindowInfo ids visible during the last commit.
+    std::unordered_set<int32_t> mVisibleWindowIds;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 2daea25..35c8b6c 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -78,8 +78,7 @@
     template <typename Visitor>
     void traverseStatesWithBuffers(Visitor&& visitor) const {
         for (const auto& state : states) {
-            if (state.state.hasBufferChanges() && state.state.hasValidBuffer() &&
-                state.state.surface) {
+            if (state.state.hasBufferChanges() && state.externalTexture && state.state.surface) {
                 visitor(state.state);
             }
         }
@@ -88,8 +87,7 @@
     template <typename Visitor>
     void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
         for (auto state = states.begin(); state != states.end();) {
-            if (state->state.hasBufferChanges() && state->state.hasValidBuffer() &&
-                state->state.surface) {
+            if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
                 int result = visitor(state->state, state->externalTexture);
                 if (result == STOP_TRAVERSAL) return;
                 if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 292083b..856fbbb 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -25,20 +25,17 @@
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 
-struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener,
-                                                                 DeathRecipient {
-    explicit WindowInfosReportedListener(
-            size_t callbackCount,
-            const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                     SpHash<gui::IWindowInfosReportedListener>>&
-                    windowInfosReportedListeners)
-          : mCallbacksPending(callbackCount),
-            mWindowInfosReportedListeners(windowInfosReportedListeners) {}
+using WindowInfosListenerVector = ftl::SmallVector<const sp<IWindowInfosListener>, 3>;
+
+struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
+                                            IBinder::DeathRecipient {
+    WindowInfosReportedListenerInvoker(WindowInfosListenerVector windowInfosListeners,
+                                       WindowInfosReportedListenerSet windowInfosReportedListeners)
+          : mCallbacksPending(windowInfosListeners.size()),
+            mWindowInfosListeners(std::move(windowInfosListeners)),
+            mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
 
     binder::Status onWindowInfosReported() override {
-        // TODO(b/222421815) There could potentially be callbacks that we don't need to wait for
-        // before calling the WindowInfosReportedListeners coming from InputWindowCommands. Filter
-        // the list of callbacks down to those from system server.
         if (--mCallbacksPending == 0) {
             for (const auto& listener : mWindowInfosReportedListeners) {
                 sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -46,6 +43,12 @@
                     listener->onWindowInfosReported();
                 }
             }
+
+            auto wpThis = wp<WindowInfosReportedListenerInvoker>::fromExisting(this);
+            for (const auto& listener : mWindowInfosListeners) {
+                sp<IBinder> binder = IInterface::asBinder(listener);
+                binder->unlinkToDeath(wpThis);
+            }
         }
         return binder::Status::ok();
     }
@@ -54,9 +57,9 @@
 
 private:
     std::atomic<size_t> mCallbacksPending;
-    std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                       SpHash<gui::IWindowInfosReportedListener>>
-            mWindowInfosReportedListeners;
+    static constexpr size_t kStaticCapacity = 3;
+    const WindowInfosListenerVector mWindowInfosListeners;
+    WindowInfosReportedListenerSet mWindowInfosReportedListeners;
 };
 
 void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
@@ -82,38 +85,81 @@
 }
 
 void WindowInfosListenerInvoker::windowInfosChanged(
-        const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
-        const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                 SpHash<gui::IWindowInfosReportedListener>>&
-                windowInfosReportedListeners) {
-    ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+        std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
+        WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall) {
+    reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
+    auto callListeners = [this, windowInfos = std::move(windowInfos),
+                          displayInfos = std::move(displayInfos)](
+                                 WindowInfosReportedListenerSet reportedListeners) mutable {
+        WindowInfosListenerVector windowInfosListeners;
+        {
+            std::scoped_lock lock(mListenersMutex);
+            for (const auto& [_, listener] : mWindowInfosListeners) {
+                windowInfosListeners.push_back(listener);
+            }
+        }
+
+        auto reportedInvoker =
+                sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners,
+                                                             std::move(reportedListeners));
+
+        for (const auto& listener : windowInfosListeners) {
+            sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+            // linkToDeath is used here to ensure that the windowInfosReportedListeners
+            // are called even if one of the windowInfosListeners dies before
+            // calling onWindowInfosReported.
+            asBinder->linkToDeath(reportedInvoker);
+
+            auto status =
+                    listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
+            if (!status.isOk()) {
+                reportedInvoker->onWindowInfosReported();
+            }
+        }
+    };
+
     {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mWindowInfosListeners) {
-            windowInfosListeners.push_back(listener);
+        std::scoped_lock lock(mMessagesMutex);
+        // If there are unacked messages and this isn't a forced call, then return immediately.
+        // If a forced window infos change doesn't happen first, the update will be sent after
+        // the WindowInfosReportedListeners are called. If a forced window infos change happens or
+        // if there are subsequent delayed messages before this update is sent, then this message
+        // will be dropped and the listeners will only be called with the latest info. This is done
+        // to reduce the amount of binder memory used.
+        if (mActiveMessageCount > 0 && !forceImmediateCall) {
+            mWindowInfosChangedDelayed = std::move(callListeners);
+            mReportedListenersDelayed.merge(reportedListeners);
+            return;
         }
+
+        mWindowInfosChangedDelayed = nullptr;
+        reportedListeners.merge(mReportedListenersDelayed);
+        mActiveMessageCount++;
+    }
+    callListeners(std::move(reportedListeners));
+}
+
+binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
+    std::function<void(WindowInfosReportedListenerSet)> callListeners;
+    WindowInfosReportedListenerSet reportedListeners;
+
+    {
+        std::scoped_lock lock{mMessagesMutex};
+        mActiveMessageCount--;
+        if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
+            return binder::Status::ok();
+        }
+
+        mActiveMessageCount++;
+        callListeners = std::move(mWindowInfosChangedDelayed);
+        mWindowInfosChangedDelayed = nullptr;
+        reportedListeners = std::move(mReportedListenersDelayed);
+        mReportedListenersDelayed.clear();
     }
 
-    auto windowInfosReportedListener = windowInfosReportedListeners.empty()
-            ? nullptr
-            : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(),
-                                                    windowInfosReportedListeners);
-    for (const auto& listener : windowInfosListeners) {
-        sp<IBinder> asBinder = IInterface::asBinder(listener);
-
-        // linkToDeath is used here to ensure that the windowInfosReportedListeners
-        // are called even if one of the windowInfosListeners dies before
-        // calling onWindowInfosReported.
-        if (windowInfosReportedListener) {
-            asBinder->linkToDeath(windowInfosReportedListener);
-        }
-
-        auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
-                                                     windowInfosReportedListener);
-        if (windowInfosReportedListener && !status.isOk()) {
-            windowInfosReportedListener->onWindowInfosReported();
-        }
-    }
+    callListeners(std::move(reportedListeners));
+    return binder::Status::ok();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index d60a9c4..4da9828 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,34 +23,42 @@
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <binder/IBinder.h>
 #include <ftl/small_map.h>
+#include <gui/SpHash.h>
 #include <utils/Mutex.h>
 
 namespace android {
 
-class SurfaceFlinger;
+using WindowInfosReportedListenerSet =
+        std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+                           gui::SpHash<gui::IWindowInfosReportedListener>>;
 
-class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+                                   public IBinder::DeathRecipient {
 public:
     void addWindowInfosListener(sp<gui::IWindowInfosListener>);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
-    void windowInfosChanged(const std::vector<gui::WindowInfo>&,
-                            const std::vector<gui::DisplayInfo>&,
-                            const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                                     SpHash<gui::IWindowInfosReportedListener>>&
-                                    windowInfosReportedListeners);
+    void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
+                            WindowInfosReportedListenerSet windowInfosReportedListeners,
+                            bool forceImmediateCall);
+
+    binder::Status onWindowInfosReported() override;
 
 protected:
     void binderDied(const wp<IBinder>& who) override;
 
 private:
-    struct WindowInfosReportedListener;
-
     std::mutex mListenersMutex;
 
     static constexpr size_t kStaticCapacity = 3;
     ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
+
+    std::mutex mMessagesMutex;
+    uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
+    std::function<void(WindowInfosReportedListenerSet)> mWindowInfosChangedDelayed
+            GUARDED_BY(mMessagesMutex);
+    WindowInfosReportedListenerSet mReportedListenersDelayed;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index a9247fe..9fac14e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -327,7 +327,6 @@
     invokeComposerHal2_4(&composer, display, outLayer);
 
     composer.executeCommands(display);
-    composer.resetCommands(display);
 
     composer.destroyLayer(display, outLayer);
     composer.destroyVirtualDisplay(display);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4304259..c3dcb85 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -125,9 +125,12 @@
                                             mFdp.ConsumeIntegral<int64_t>(),
                                             mFdp.ConsumeIntegral<int64_t>());
 
-    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share());
-    layer->onLayerDisplayed(
-            ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
+    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+                            ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
+    layer->onLayerDisplayed(ftl::yield<FenceResult>(
+                                    base::unexpected(mFdp.ConsumeIntegral<status_t>()))
+                                    .share(),
+                            ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
 
     layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
     layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 26dbc76..79886bd 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1224,4 +1224,75 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
+TEST_F(LayerCallbackTest, SetNullBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+                              /*setBackgroundColor=*/false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+
+    transaction.setBuffer(layer, nullptr);
+    transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED_NULL,
+                            ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+
+    err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+                          /*setBackgroundColor=*/false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+}
+
+TEST_F(LayerCallbackTest, SetNullBufferOnLayerWithoutBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+    Transaction transaction;
+    transaction.setBuffer(layer, nullptr);
+    CallbackHelper callback;
+    transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 0b8c51e..b8068f7 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1636,6 +1636,65 @@
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBuffer) {
+    const Rect bounds(0, 0, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
+
+    ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bounds, Color::GREEN));
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("before null buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::GREEN);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer removes buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("after null buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBufferOnLayerWithoutBuffer) {
+    const Rect bounds(0, 0, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    {
+        SCOPED_TRACE("starting state");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer has no effect");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer has no effect");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 88d39db..e4f49e8 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -466,7 +466,8 @@
 
     updateBackgroundColor(1, 0.5);
     UPDATE_AND_VERIFY(hierarchyBuilder);
-    auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+    auto hierarchy = hierarchyBuilder.getPartialHierarchy(1, /*childrenOnly=*/true);
+    auto bgLayerId = hierarchy.mChildren.front().first->getLayer()->id;
     std::vector<uint32_t> expectedTraversalPath = {1,   bgLayerId, 11,   111, 12,
                                                    121, 122,       1221, 13,  2};
     EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 14b8e4b..97ef5a2 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -36,13 +36,13 @@
 class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener {
 public:
     void onLayerAdded(const RequestedLayerState& layer) override {
-        mActualLayersAdded.emplace(layer.id);
+        mActualLayersAdded.push_back(layer.id);
     };
     void onLayerDestroyed(const RequestedLayerState& layer) override {
         mActualLayersDestroyed.emplace(layer.id);
     };
 
-    void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) {
+    void expectLayersAdded(const std::vector<uint32_t>& expectedLayersAdded) {
         EXPECT_EQ(expectedLayersAdded, mActualLayersAdded);
         mActualLayersAdded.clear();
     }
@@ -51,7 +51,7 @@
         mActualLayersDestroyed.clear();
     }
 
-    std::unordered_set<uint32_t> mActualLayersAdded;
+    std::vector<uint32_t> mActualLayersAdded;
     std::unordered_set<uint32_t> mActualLayersDestroyed;
 };
 
@@ -318,7 +318,8 @@
 
     EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     lifecycleManager.commitChanges();
-    auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
     listener->expectLayersAdded({1, bgLayerId});
     listener->expectLayersDestroyed({});
     EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf);
@@ -352,7 +353,8 @@
 
     EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     lifecycleManager.commitChanges();
-    auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
     listener->expectLayersAdded({1, bgLayerId});
     listener->expectLayersDestroyed({bgLayerId});
 }
@@ -381,7 +383,8 @@
 
     EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     lifecycleManager.commitChanges();
-    auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
     listener->expectLayersAdded({1, bgLayerId});
     listener->expectLayersDestroyed({1, bgLayerId});
 }
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 0c43831..dc76b4c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -384,23 +384,4 @@
     }
 }
 
-TEST_F(SchedulerTest, changingPacesetterChangesVsyncSchedule) {
-    // Add a second display so we can change the pacesetter.
-    mScheduler->registerDisplay(kDisplayId2,
-                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
-                                                                      kDisplay2Mode60->getId()));
-    // Ensure that the pacesetter is the one we expect.
-    mScheduler->setPacesetterDisplay(kDisplayId1);
-
-    // Switching to the other will call onNewVsyncSchedule.
-    EXPECT_CALL(*mEventThread, onNewVsyncSchedule(mScheduler->getVsyncSchedule(kDisplayId2)))
-            .Times(1);
-    mScheduler->setPacesetterDisplay(kDisplayId2);
-}
-
-TEST_F(SchedulerTest, promotingSamePacesetterDoesNotChangeVsyncSchedule) {
-    EXPECT_CALL(*mEventThread, onNewVsyncSchedule(_)).Times(0);
-    mScheduler->setPacesetterDisplay(kDisplayId1);
-}
-
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 6a9c970..1cc9ba4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -17,12 +17,60 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <scheduler/Fps.h>
+
 #include "DisplayTransactionTestHelpers.h"
+#include "FpsOps.h"
 
 namespace android {
 namespace {
 
-class CreateDisplayTest : public DisplayTransactionTest {};
+class CreateDisplayTest : public DisplayTransactionTest {
+public:
+    void createDisplayWithRequestedRefreshRate(const String8& name, uint64_t displayId,
+                                               float pacesetterDisplayRefreshRate,
+                                               float requestedRefreshRate,
+                                               float expectedAdjustedRefreshRate) {
+        // --------------------------------------------------------------------
+        // Call Expectations
+
+        // --------------------------------------------------------------------
+        // Invocation
+
+        sp<IBinder> displayToken = mFlinger.createDisplay(name, false, requestedRefreshRate);
+
+        // --------------------------------------------------------------------
+        // Postconditions
+
+        // The display should have been added to the current state
+        ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+        const auto& display = getCurrentDisplayState(displayToken);
+        EXPECT_TRUE(display.isVirtual());
+        EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
+        EXPECT_EQ(name.string(), display.displayName);
+
+        std::optional<VirtualDisplayId> vid =
+                DisplayId::fromValue<VirtualDisplayId>(displayId | DisplayId::FLAG_VIRTUAL);
+        ASSERT_TRUE(vid.has_value());
+
+        sp<DisplayDevice> device =
+                mFlinger.createVirtualDisplayDevice(displayToken, *vid, requestedRefreshRate);
+        EXPECT_TRUE(device->isVirtual());
+        device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
+        // verifying desired value
+        EXPECT_EQ(device->getAdjustedRefreshRate(), Fps::fromValue(expectedAdjustedRefreshRate));
+        // verifying rounding up
+        if (requestedRefreshRate < pacesetterDisplayRefreshRate) {
+            EXPECT_GE(device->getAdjustedRefreshRate(), Fps::fromValue(requestedRefreshRate));
+        } else {
+            EXPECT_EQ(device->getAdjustedRefreshRate(),
+                      Fps::fromValue(pacesetterDisplayRefreshRate));
+        }
+
+        // --------------------------------------------------------------------
+        // Cleanup conditions
+    }
+};
 
 TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
     const String8 name("virtual.test");
@@ -84,5 +132,82 @@
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 }
 
+// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRate0) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 0.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting negative refresh rate, will be ignored, same as requesting 0
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNegative) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = -60.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a higher refresh rate than the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateHigh) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 90.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting the same refresh rate as the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateSame) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 60.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a divisor (30) of the pacesetter (60) should be honored
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 30.f;
+    const float kExpectedAdjustedRefreshRate = 30.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (45) of the pacesetter (120) should round up to a divisor (60)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 45.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (75) of the pacesetter (120) should round up to pacesetter (120)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisorMax) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 75.f;
+    const float kExpectedAdjustedRefreshRate = 120.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index f1a5fc4..3b6a987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -65,10 +65,6 @@
 
     auto refreshRateSelector() { return pacesetterSelectorPtr(); }
 
-    const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
-        return mRefreshRateSelectors;
-    }
-
     void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
         registerDisplay(displayId, std::move(selectorPtr),
                         std::make_unique<mock::VsyncController>(),
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6f48df8..cfa366f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -48,10 +48,12 @@
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
+#include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 namespace renderengine {
@@ -375,8 +377,8 @@
 
     void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
-    auto createDisplay(const String8& displayName, bool secure) {
-        return mFlinger->createDisplay(displayName, secure);
+    auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
+        return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
     }
 
     auto destroyDisplay(const sp<IBinder>& displayToken) {
@@ -525,6 +527,24 @@
         mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
     }
 
+    sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
+                                                 VirtualDisplayId displayId,
+                                                 float requestedRefreshRate) {
+        constexpr ui::Size kResolution = {1080, 1920};
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(mFlinger->getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(displayId)
+                                      .setPixels(kResolution)
+                                      .setPowerAdvisor(&mPowerAdvisor)
+                                      .build());
+        DisplayDeviceCreationArgs creationArgs(mFlinger, mFlinger->getHwComposer(), displayToken,
+                                               compositionDisplay);
+        creationArgs.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
+        creationArgs.nativeWindow = sp<mock::NativeWindow>::make();
+        return sp<DisplayDevice>::make(creationArgs);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -962,6 +982,7 @@
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 03c4e71..dbb7c6c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -274,6 +274,34 @@
     EXPECT_EQ(nullptr, ret.get());
 }
 
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mEmptyBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(BufferData& bufferData)
+          : mWidth(bufferData.getWidth()),
+            mHeight(bufferData.getHeight()),
+            mId(bufferData.getId()),
+            mPixelFormat(bufferData.getPixelFormat()),
+            mUsage(bufferData.getUsage()) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    void remapBuffer() override {}
+    ~FakeExternalTexture() = default;
+};
+
 class LatchUnsignaledTest : public TransactionApplicationTest {
 public:
     void TearDown() override {
@@ -346,7 +374,11 @@
             std::vector<ResolvedComposerState> resolvedStates;
             resolvedStates.reserve(transaction.states.size());
             for (auto& state : transaction.states) {
-                resolvedStates.emplace_back(std::move(state));
+                ResolvedComposerState resolvedState;
+                resolvedState.state = std::move(state.state);
+                resolvedState.externalTexture =
+                        std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData);
+                resolvedStates.emplace_back(resolvedState);
             }
 
             TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5dc3490..d3fb9fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -55,7 +55,6 @@
                  std::vector<aidl::android::hardware::graphics::composer3::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
     MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&));
-    MOCK_METHOD1(resetCommands, void(Display));
     MOCK_METHOD1(executeCommands, Error(Display));
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
     MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 53f3daf..971a0b9 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "PowerHalControllerBenchmarks"
+#define LOG_TAG "VibratorHalControllerBenchmarks"
 
 #include <benchmark/benchmark.h>
 #include <vibratorservice/VibratorHalController.h>
@@ -183,7 +183,7 @@
         return;
     }
 
-    auto duration = 6000s;
+    auto duration = 60s;
     auto callback = []() {};
     auto amplitude = 1.0f;
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index dec3b20..5965953 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -227,6 +227,10 @@
     android::sp<ANativeWindow> window;
     VkSwapchainKHR swapchain_handle;
     uint64_t consumer_usage;
+
+    // Indicate whether this surface has been used by a swapchain, no matter the
+    // swapchain is still current or has been destroyed.
+    bool used_by_swapchain;
 };
 
 VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -252,27 +256,31 @@
     Swapchain(Surface& surface_,
               uint32_t num_images_,
               VkPresentModeKHR present_mode,
-              int pre_transform_)
+              int pre_transform_,
+              int64_t refresh_duration_)
         : surface(surface_),
           num_images(num_images_),
           mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
           pre_transform(pre_transform_),
           frame_timestamps_enabled(false),
+          refresh_duration(refresh_duration_),
           acquire_next_image_timeout(-1),
           shared(IsSharedPresentMode(present_mode)) {
-        ANativeWindow* window = surface.window.get();
-        native_window_get_refresh_cycle_duration(
-            window,
-            &refresh_duration);
     }
-    uint64_t get_refresh_duration()
+
+    VkResult get_refresh_duration(uint64_t& outRefreshDuration)
     {
         ANativeWindow* window = surface.window.get();
-        native_window_get_refresh_cycle_duration(
+        int err = native_window_get_refresh_cycle_duration(
             window,
             &refresh_duration);
-        return static_cast<uint64_t>(refresh_duration);
-
+        if (err != android::OK) {
+            ALOGE("%s:native_window_get_refresh_cycle_duration failed: %s (%d)",
+                __func__, strerror(-err), err );
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        outRefreshDuration = refresh_duration;
+        return VK_SUCCESS;
     }
 
     Surface& surface;
@@ -597,6 +605,7 @@
 
     surface->window = pCreateInfo->window;
     surface->swapchain_handle = VK_NULL_HANDLE;
+    surface->used_by_swapchain = false;
     int err = native_window_get_consumer_usage(surface->window.get(),
                                                &surface->consumer_usage);
     if (err != android::OK) {
@@ -1390,14 +1399,20 @@
     // orphans the previous buffers, getting us back to the state where we can
     // dequeue all buffers.
     //
+    // This is not necessary if the surface was never used previously.
+    //
     // TODO(http://b/134186185) recycle swapchain images more efficiently
     ANativeWindow* window = surface.window.get();
-    err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
-             strerror(-err), err);
-    err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
-             strerror(-err), err);
+    if (surface.used_by_swapchain) {
+        err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+        ALOGW_IF(err != android::OK,
+                 "native_window_api_disconnect failed: %s (%d)", strerror(-err),
+                 err);
+        err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+        ALOGW_IF(err != android::OK,
+                 "native_window_api_connect failed: %s (%d)", strerror(-err),
+                 err);
+    }
 
     err =
         window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, nsecs_t{-1});
@@ -1626,6 +1641,13 @@
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
+    int64_t refresh_duration;
+    err = native_window_get_refresh_cycle_duration(window, &refresh_duration);
+    if (err != android::OK) {
+        ALOGE("native_window_get_refresh_cycle_duration query failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
     // -- Allocate our Swapchain object --
     // After this point, we must deallocate the swapchain on error.
 
@@ -1636,8 +1658,8 @@
         return VK_ERROR_OUT_OF_HOST_MEMORY;
     Swapchain* swapchain = new (mem)
         Swapchain(surface, num_images, create_info->presentMode,
-                  TranslateVulkanToNativeTransform(create_info->preTransform));
-
+                  TranslateVulkanToNativeTransform(create_info->preTransform),
+                  refresh_duration);
     VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wold-style-cast"
@@ -1776,6 +1798,7 @@
     android::GraphicsEnv::getInstance().setTargetStats(
         android::GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN);
 
+    surface.used_by_swapchain = true;
     surface.swapchain_handle = HandleFromSwapchain(swapchain);
     *swapchain_handle = surface.swapchain_handle;
     return VK_SUCCESS;
@@ -2308,9 +2331,7 @@
     ATRACE_CALL();
 
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
-    VkResult result = VK_SUCCESS;
-
-    pDisplayTimingProperties->refreshDuration = swapchain.get_refresh_duration();
+    VkResult result = swapchain.get_refresh_duration(pDisplayTimingProperties->refreshDuration);
 
     return result;
 }