Merge changes from topic "revert-23316821-WWTNIVUOBO" into udc-dev am: 01b8e815f1 am: 96000643d3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/23421325

Change-Id: Iff30ef1302b40f2e23db96ca8637ace8a2909c8f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index cd2652c..790556c 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -75,6 +75,23 @@
         return EXIT_FAILURE;
     }
 
+    // Wait a little while for dumpstatez to stop if it is running
+    bool dumpstate_running = false;
+    for (int i = 0; i < 20; i++) {
+        char buf[PROPERTY_VALUE_MAX];
+        property_get("init.svc.dumpstatez", buf, "");
+        dumpstate_running = strcmp(buf, "running") == 0;
+
+        if (!dumpstate_running) break;
+
+        sleep(1);
+    }
+
+    if (dumpstate_running) {
+        fprintf(stderr, "FAIL:dumpstatez service is already running\n");
+        return EXIT_FAILURE;
+    }
+
     // TODO: code below was copy-and-pasted from bugreport.cpp (except by the
     // timeout value);
     // should be reused instead.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5dbf7ac..7e3d273 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2602,7 +2602,11 @@
 }
 
 static void register_sig_handler() {
-    signal(SIGPIPE, SIG_IGN);
+    signal(SIGPIPE, [](int) {
+        MYLOGE("Connection with client lost, canceling.");
+        ds.Cancel();
+        abort();
+    });
 }
 
 bool Dumpstate::FinishZipFile() {
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 07f73b9..61fe316 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -77,10 +77,8 @@
     },
 }
 
-cc_test {
-    name: "installd_service_test",
-    test_suites: ["device-tests"],
-    srcs: ["installd_service_test.cpp"],
+cc_defaults {
+    name: "installd_service_test_defaults",
     cflags: [
         "-Wall",
         "-Werror",
@@ -106,8 +104,6 @@
         "liblogwrap",
         "libc++fs",
     ],
-    test_config: "installd_service_test.xml",
-
     product_variables: {
         arc: {
             exclude_srcs: [
@@ -125,6 +121,14 @@
 }
 
 cc_test {
+    name: "installd_service_test",
+    test_suites: ["device-tests"],
+    srcs: ["installd_service_test.cpp"],
+    defaults: ["installd_service_test_defaults"],
+    test_config: "installd_service_test.xml",
+}
+
+cc_test {
     name: "installd_dexopt_test",
     test_suites: ["device-tests"],
     srcs: ["installd_dexopt_test.cpp"],
@@ -209,3 +213,19 @@
         "liblog",
     ],
 }
+
+cc_fuzz {
+    name: "installd_service_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+        "installd_service_test_defaults",
+    ],
+    srcs: ["fuzzers/InstalldServiceFuzzer.cpp"],
+    fuzz_config: {
+        cc: [
+            "android-package-manager-team@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+}
diff --git a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
new file mode 100644
index 0000000..b1c6940
--- /dev/null
+++ b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "InstalldNativeService.h"
+#include "dexopt.h"
+
+using ::android::fuzzService;
+using ::android::sp;
+using ::android::installd::InstalldNativeService;
+
+namespace android {
+namespace installd {
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char* oat_dir, const char* apk_path,
+                             const char* instruction_set) {
+    return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char* apk_path,
+                              const char* instruction_set) {
+    return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char* src, const char* instruction_set) {
+    return create_cache_path_default(path, src, instruction_set);
+}
+
+bool force_compile_without_image() {
+    return false;
+}
+
+} // namespace installd
+} // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto service = sp<InstalldNativeService>::make();
+    fuzzService(service, FuzzedDataProvider(data, size));
+    return 0;
+}
\ No newline at end of file
diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp
index af85666..d0e4b74 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -37,4 +37,8 @@
         "include",
     ],
     min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.neuralnetworks",
+    ],
 }
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 63f3821..e30cbd5 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -301,7 +301,7 @@
     }
 
     if (!out && startIfNotFound) {
-        tryStartService(name);
+        tryStartService(ctx, name);
     }
 
     if (out) {
@@ -372,8 +372,10 @@
     }
 
     auto it = mNameToService.find(name);
+    bool prevClients = false;
     if (it != mNameToService.end()) {
         const Service& existing = it->second;
+        prevClients = existing.hasClients;
 
         // We could do better than this because if the other service dies, it
         // may not have an entry here. However, this case is unlikely. We are
@@ -401,10 +403,13 @@
             .binder = binder,
             .allowIsolated = allowIsolated,
             .dumpPriority = dumpPriority,
+            .hasClients = prevClients, // see b/279898063, matters if existing callbacks
+            .guaranteeClient = false,  // handled below
             .ctx = ctx,
     };
 
     if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
+        // TODO: this is only needed once
         // See also getService - handles case where client never gets the service,
         // we want the service to quit.
         mNameToService[name].guaranteeClient = true;
@@ -633,6 +638,14 @@
 void ServiceManager::binderDied(const wp<IBinder>& who) {
     for (auto it = mNameToService.begin(); it != mNameToService.end();) {
         if (who == it->second.binder) {
+            // TODO: currently, this entry contains the state also
+            // associated with mNameToClientCallback. If we allowed
+            // other processes to register client callbacks, we
+            // would have to preserve hasClients (perhaps moving
+            // that state into mNameToClientCallback, which is complicated
+            // because those callbacks are associated w/ particular binder
+            // objects, though they are indexed by name now, they may
+            // need to be indexed by binder at that point).
             it = mNameToService.erase(it);
         } else {
             ++it;
@@ -648,10 +661,11 @@
     }
 }
 
-void ServiceManager::tryStartService(const std::string& name) {
-    ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not "
-          "configured to be a lazy service, it may be stuck starting or still starting).",
-          name.c_str());
+void ServiceManager::tryStartService(const Access::CallingContext& ctx, const std::string& name) {
+    ALOGI("Since '%s' could not be found (requested by debug pid %d), trying to start it as a lazy "
+          "AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or "
+          "still starting).",
+          name.c_str(), ctx.debugPid);
 
     std::thread([=] {
         if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
@@ -700,7 +714,10 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
     }
 
-    // make sure all callbacks have been told about a consistent state - b/278038751
+    // WARNING: binderDied makes an assumption about this. If we open up client
+    // callbacks to other services, certain race conditions may lead to services
+    // getting extra client callback notifications.
+    // Make sure all callbacks have been told about a consistent state - b/278038751
     if (serviceIt->second.hasClients) {
         cb->onClients(service, true);
     }
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 3aa6731..3b925a4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -67,7 +67,7 @@
     void clear();
 
 protected:
-    virtual void tryStartService(const std::string& name);
+    virtual void tryStartService(const Access::CallingContext& ctx, const std::string& name);
 
 private:
     struct Service {
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index c1a04dd..86a45e61 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -131,7 +131,9 @@
     }
 
     IPCThreadState::self()->setTheContextObject(manager);
-    ps->becomeContextManager();
+    if (!ps->becomeContextManager()) {
+        LOG(FATAL) << "Could not become context manager";
+    }
 
     sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 
diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc
index b927c01..6354fd7 100644
--- a/cmds/servicemanager/servicemanager.recovery.rc
+++ b/cmds/servicemanager/servicemanager.recovery.rc
@@ -1,5 +1,6 @@
 service servicemanager /system/bin/servicemanager
     disabled
     group system readproc
+    user root
     onrestart setprop servicemanager.ready false
     seclabel u:r:servicemanager:s0
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index cae32e3..97e500d 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -27,11 +27,14 @@
 #include "Access.h"
 #include "ServiceManager.h"
 
-using android::sp;
 using android::Access;
 using android::BBinder;
 using android::IBinder;
 using android::ServiceManager;
+using android::sp;
+using android::base::EndsWith;
+using android::base::GetProperty;
+using android::base::StartsWith;
 using android::binder::Status;
 using android::os::BnServiceCallback;
 using android::os::IServiceManager;
@@ -62,7 +65,7 @@
 class MockServiceManager : public ServiceManager {
  public:
     MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {}
-    MOCK_METHOD1(tryStartService, void(const std::string& name));
+    MOCK_METHOD2(tryStartService, void(const Access::CallingContext&, const std::string& name));
 };
 
 static sp<ServiceManager> getPermissiveServiceManager() {
@@ -77,9 +80,11 @@
     return sm;
 }
 
-static bool isCuttlefish() {
-    return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""),
-                                     "vsoc_");
+// Determines if test device is a cuttlefish phone device
+static bool isCuttlefishPhone() {
+    auto device = GetProperty("ro.product.vendor.device", "");
+    auto product = GetProperty("ro.product.vendor.name", "");
+    return StartsWith(device, "vsoc_") && EndsWith(product, "_phone");
 }
 
 TEST(AddService, HappyHappy) {
@@ -314,7 +319,7 @@
 }
 
 TEST(Vintf, UpdatableViaApex) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::optional<std::string> updatableViaApex;
@@ -326,7 +331,7 @@
 }
 
 TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::optional<std::string> updatableViaApex;
@@ -337,7 +342,7 @@
 }
 
 TEST(Vintf, GetUpdatableNames) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::vector<std::string> names;
@@ -348,7 +353,7 @@
 }
 
 TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::vector<std::string> names;
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
index d36c958..5966cba 100644
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<!-- Feature for devices that support satellite communication via satellite vendor service APIs. -->
+<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
 <permissions>
     <feature name="android.hardware.telephony.satellite" />
 </permissions>
diff --git a/include/input/Input.h b/include/input/Input.h
index fe0c775..d4750dd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -269,6 +269,7 @@
 
     // Indicates that the key represents a special gesture that has been detected by
     // the touch firmware or driver.  Causes touch events from the same device to be canceled.
+    // This policy flag prevents key events from changing touch mode state.
     POLICY_FLAG_GESTURE = 0x00000008,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h
index 37fe5af..d2747d6 100644
--- a/include/input/RingBuffer.h
+++ b/include/input/RingBuffer.h
@@ -24,7 +24,6 @@
 #include <type_traits>
 #include <utility>
 
-#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
 namespace android {
@@ -277,15 +276,16 @@
 
     // Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
     size_type bufferIndex(size_type elementIndex) const {
-        CHECK_LE(elementIndex, size());
+        if (elementIndex > size()) {
+            abort();
+        }
         size_type index = mBegin + elementIndex;
         if (index >= capacity()) {
             index -= capacity();
         }
-        CHECK_LT(index, capacity())
-                << android::base::StringPrintf("Invalid index calculated for element (%zu) "
-                                               "in buffer of size %zu",
-                                               elementIndex, size());
+        if (index >= capacity()) {
+            abort();
+        }
         return index;
     }
 
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index f3c201e..b78f63e 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -88,7 +88,7 @@
     VelocityControl();
 
     /* Gets the various parameters. */
-    VelocityControlParameters& getParameters();
+    const VelocityControlParameters& getParameters() const;
 
     /* Sets the various parameters. */
     void setParameters(const VelocityControlParameters& parameters);
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index da97c3e..b58feac 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <input/Input.h>
+#include <input/RingBuffer.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
 #include <map>
@@ -31,6 +32,8 @@
  */
 class VelocityTracker {
 public:
+    static const size_t MAX_DEGREE = 4;
+
     enum class Strategy : int32_t {
         DEFAULT = -1,
         MIN = 0,
@@ -47,23 +50,6 @@
         MAX = LEGACY,
     };
 
-    struct Estimator {
-        static const size_t MAX_DEGREE = 4;
-
-        // Estimator time base.
-        nsecs_t time = 0;
-
-        // Polynomial coefficients describing motion.
-        std::array<float, MAX_DEGREE + 1> coeff{};
-
-        // Polynomial degree (number of coefficients), or zero if no information is
-        // available.
-        uint32_t degree = 0;
-
-        // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
-        float confidence = 0;
-    };
-
     /*
      * Contains all available velocity data from a VelocityTracker.
      */
@@ -124,11 +110,6 @@
     // [-maxVelocity, maxVelocity], inclusive.
     ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
 
-    // Gets an estimator for the recent movements of the specified pointer id for the given axis.
-    // Returns false and clears the estimator if there is no information available
-    // about the pointer.
-    std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const;
-
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); }
 
@@ -169,14 +150,48 @@
 
     virtual void clearPointer(int32_t pointerId) = 0;
     virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0;
-    virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0;
+    virtual std::optional<float> getVelocity(int32_t pointerId) const = 0;
 };
 
+/**
+ * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data
+ * points when getting velocity.
+ */
+class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy {
+public:
+    AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd);
+
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+    void clearPointer(int32_t pointerId) override;
+
+protected:
+    struct Movement {
+        nsecs_t eventTime;
+        float position;
+    };
+
+    // Number of samples to keep.
+    // If different strategies would like to maintain different history size, we can make this a
+    // protected const field.
+    static constexpr uint32_t HISTORY_SIZE = 20;
+
+    /**
+     * Duration, in nanoseconds, since the latest movement where a movement may be considered for
+     * velocity calculation.
+     */
+    const nsecs_t mHorizonNanos;
+    /**
+     * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each
+     * addition of a new movement.
+     */
+    const bool mMaintainHorizonDuringAdd;
+    std::map<int32_t /*pointerId*/, RingBuffer<Movement>> mMovements;
+};
 
 /*
  * Velocity tracker algorithm based on least-squares linear regression.
  */
-class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     enum class Weighting {
         // No weights applied.  All data points are equally reliable.
@@ -193,13 +208,11 @@
         RECENT,
     };
 
-    // Degree must be no greater than Estimator::MAX_DEGREE.
+    // Degree must be no greater than VelocityTracker::MAX_DEGREE.
     LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE);
     ~LeastSquaresVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -207,23 +220,19 @@
     // changes in direction.
     static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static const uint32_t HISTORY_SIZE = 20;
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
     float chooseWeight(int32_t pointerId, uint32_t index) const;
+    /**
+     * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`).
+     * The provided container of movements shall NOT be empty, and shall have the movements in
+     * chronological order.
+     */
+    std::optional<float> solveUnweightedLeastSquaresDeg2(
+            const RingBuffer<Movement>& movements) const;
 
     const uint32_t mDegree;
     const Weighting mWeighting;
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
-
 /*
  * Velocity tracker algorithm that uses an IIR filter.
  */
@@ -235,7 +244,7 @@
 
     void clearPointer(int32_t pointerId) override;
     void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Current state estimate for a particular pointer.
@@ -252,49 +261,33 @@
 
     void initState(State& state, nsecs_t eventTime, float pos) const;
     void updateState(State& state, nsecs_t eventTime, float pos) const;
-    void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
 };
 
 
 /*
  * Velocity tracker strategy used prior to ICS.
  */
-class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     LegacyVelocityTrackerStrategy();
     ~LegacyVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Oldest sample to consider when calculating the velocity.
     static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static const uint32_t HISTORY_SIZE = 20;
-
     // The minimum duration between samples when estimating velocity.
     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
-class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     ImpulseVelocityTrackerStrategy(bool deltaValues);
     ~ImpulseVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -302,21 +295,10 @@
     // changes in direction.
     static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static constexpr size_t HISTORY_SIZE = 20;
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
     // Whether or not the input movement values for the strategy come in the form of delta values.
     // If the input values are not deltas, the strategy needs to calculate deltas as part of its
     // velocity calculation.
     const bool mDeltaValues;
-
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
 } // namespace android
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 5e539f2..1a9766d 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -72,6 +72,7 @@
         "//apex_available:platform",
         "com.android.media",
         "com.android.media.swcodec",
+        "com.android.neuralnetworks",
     ],
 
 }
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 49dd9c7..1dff38c 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -144,10 +144,6 @@
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
     product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
-        },
-
         debuggable: {
             cflags: [
                 "-DBINDER_RPC_DEV_SERVERS",
@@ -285,14 +281,6 @@
     cflags: [
         "-DBINDER_WITH_KERNEL_IPC",
     ],
-    arch: {
-        // TODO(b/254713216): undefined symbol in BufferedTextOutput::getBuffer
-        riscv64: {
-            lto: {
-                thin: false,
-            },
-        },
-    },
 }
 
 cc_library {
@@ -548,6 +536,7 @@
         ":__subpackages__",
         "//packages/modules/Virtualization/javalib/jni",
         "//packages/modules/Virtualization/vm_payload",
+        "//packages/modules/Virtualization/demo_native",
         "//device/google/cuttlefish/shared/minidroid:__subpackages__",
         "//system/software_defined_vehicle:__subpackages__",
     ],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 3e49656..0f4a6ca 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -58,15 +58,15 @@
 
 // global b/c b/230079120 - consistent symbol table
 #ifdef BINDER_RPC_DEV_SERVERS
-bool kEnableRpcDevServers = true;
+constexpr bool kEnableRpcDevServers = true;
 #else
-bool kEnableRpcDevServers = false;
+constexpr bool kEnableRpcDevServers = false;
 #endif
 
 #ifdef BINDER_ENABLE_RECORDING
-bool kEnableRecording = true;
+constexpr bool kEnableRecording = true;
 #else
-bool kEnableRecording = false;
+constexpr bool kEnableRecording = false;
 #endif
 
 // Log any reply transactions for which the data exceeds this size
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f66993f..7644806 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -324,6 +324,10 @@
     return *registrarInstance;
 }
 
+LazyServiceRegistrar LazyServiceRegistrar::createExtraTestInstance() {
+    return LazyServiceRegistrar();
+}
+
 status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
                                                bool allowIsolated, int dumpFlags) {
     if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 8fe1d2b..3da06ba 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -78,7 +78,7 @@
         if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
             ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error  %s", name,
                   SEAL_FLAGS, strerror(errno));
-            munmap(mBase, mSize);
+            if (mNeedUnmap) munmap(mBase, mSize);
             mBase = nullptr;
             mSize = 0;
             close(fd);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 5f1f506..3fa6867 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -104,14 +104,7 @@
     return access("/vendor/bin/vndservicemanager", R_OK) == 0;
 }
 
-sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
-{
-#ifdef BINDER_IPC_32BIT
-    LOG_ALWAYS_FATAL("32-bit binder IPC is not supported for new devices starting in Android P. If "
-                     "you do need to use this mode, please see b/232423610 or file an issue with "
-                     "AOSP upstream as otherwise this will be removed soon.");
-#endif
-
+sp<ProcessState> ProcessState::init(const char* driver, bool requireDefault) {
     if (driver == nullptr) {
         std::lock_guard<std::mutex> l(gProcessMutex);
         if (gProcess) {
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 1c76135..44a9e3b 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -161,17 +161,6 @@
 constexpr uint32_t kMaxChunkDataSize = 0xfffffff0;
 typedef uint64_t transaction_checksum_t;
 
-static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut,
-                                             transaction_checksum_t* sum) {
-    if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
-        LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get();
-        return android::UNKNOWN_ERROR;
-    }
-
-    *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut);
-    return android::NO_ERROR;
-}
-
 std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
     RecordedTransaction t;
     ChunkDescriptor chunk;
@@ -192,11 +181,13 @@
             LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
             return std::nullopt;
         }
-        transaction_checksum_t checksum = 0;
-        if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
-            LOG(ERROR) << "Failed to read chunk descriptor.";
+
+        if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
+            LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
+                       << strerror(errno);
             return std::nullopt;
         }
+        transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);
 
         fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
         if (fdCurrentPosition == -1) {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 9282856..55fc16d 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -81,6 +81,7 @@
     auto aiStart = InetSocketAddress::getAddrInfo(address, port);
     if (aiStart == nullptr) return UNKNOWN_ERROR;
     for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        if (ai->ai_addr == nullptr) continue;
         InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port);
         if (status_t status = setupSocketServer(socketAddress); status != OK) {
             continue;
@@ -123,8 +124,13 @@
     return mMaxThreads;
 }
 
-void RpcServer::setProtocolVersion(uint32_t version) {
+bool RpcServer::setProtocolVersion(uint32_t version) {
+    if (!RpcState::validateProtocolVersion(version)) {
+        return false;
+    }
+
     mProtocolVersion = version;
+    return true;
 }
 
 void RpcServer::setSupportedFileDescriptorTransportModes(
@@ -148,7 +154,7 @@
     mRootObjectWeak = binder;
 }
 void RpcServer::setPerSessionRootObject(
-        std::function<sp<IBinder>(const void*, size_t)>&& makeObject) {
+        std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& makeObject) {
     RpcMutexLockGuard _l(mLock);
     mRootObject.clear();
     mRootObjectWeak.clear();
@@ -161,6 +167,12 @@
     mConnectionFilter = std::move(filter);
 }
 
+void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) {
+    RpcMutexLockGuard _l(mLock);
+    LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started");
+    mServerSocketModifier = std::move(modifier);
+}
+
 sp<IBinder> RpcServer::getRootObject() {
     RpcMutexLockGuard _l(mLock);
     bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -335,6 +347,8 @@
         mJoinThread.reset();
     }
 
+    mServer = RpcTransportFd();
+
     LOG_RPC_DETAIL("Finished waiting on shutdown.");
 
     mShutdownTrigger = nullptr;
@@ -501,7 +515,8 @@
             // if null, falls back to server root
             sp<IBinder> sessionSpecificRoot;
             if (server->mRootObjectFactory != nullptr) {
-                sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen);
+                sessionSpecificRoot =
+                        server->mRootObjectFactory(wp<RpcSession>(session), addr.data(), addrLen);
                 if (sessionSpecificRoot == nullptr) {
                     ALOGE("Warning: server returned null from root object factory");
                 }
@@ -556,6 +571,14 @@
         ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
         return -savedErrno;
     }
+
+    {
+        RpcMutexLockGuard _l(mLock);
+        if (mServerSocketModifier != nullptr) {
+            mServerSocketModifier(socket_fd);
+        }
+    }
+
     if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
         int savedErrno = errno;
         ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fbad0f7..c3dee16 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -104,11 +104,7 @@
 }
 
 bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
-    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
-        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
-        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
-              "is %u).",
-              version, RPC_WIRE_PROTOCOL_VERSION);
+    if (!RpcState::validateProtocolVersion(version)) {
         return false;
     }
 
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 03fa699..ff35f5f 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -398,6 +398,18 @@
     return OK;
 }
 
+bool RpcState::validateProtocolVersion(uint32_t version) {
+    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
+              "version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+    return true;
+}
+
 status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                           const sp<RpcSession>& session, uint32_t* version) {
     RpcNewSessionResponse response;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 0e23ea7..1fe71a5 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -63,6 +63,8 @@
     RpcState();
     ~RpcState();
 
+    [[nodiscard]] static bool validateProtocolVersion(uint32_t version);
+
     [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                                   const sp<RpcSession>& session, uint32_t* version);
     [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index cd067bf..f3575cc 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -29,8 +29,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport with TLS disabled.
 class RpcTransportRaw : public RpcTransport {
 public:
@@ -96,8 +94,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newServerCtx() const {
     return std::make_unique<RpcTransportCtxRaw>();
 }
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index d5a6da2..0c81d83 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -31,8 +31,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport for writing Trusty IPC clients in Android.
 class RpcTransportTipcAndroid : public RpcTransport {
 public:
@@ -217,8 +215,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const {
     return std::make_unique<RpcTransportCtxTipcAndroid>();
 }
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 3e98ecc..785f6ce 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -275,6 +275,8 @@
     bssl::UniquePtr<SSL> mSsl;
 };
 
+} // namespace
+
 class RpcTransportTls : public RpcTransport {
 public:
     RpcTransportTls(RpcTransportFd socket, Ssl ssl)
@@ -411,7 +413,8 @@
 }
 
 // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|.
-bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) {
+static bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket,
+                                FdTrigger* fdTrigger) {
     bssl::UniquePtr<BIO> bio = newSocketBio(socket.fd);
     TEST_AND_RETURN(false, bio != nullptr);
     auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get());
@@ -540,8 +543,6 @@
     }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
     return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier,
                                                                          mAuth.get());
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 0e8e187..199574e 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,9 +16,15 @@
       "name": "binderDriverInterfaceTest"
     },
     {
+      "name": "binderRecordReplayTest"
+    },
+    {
       "name": "binderHostDeviceTest"
     },
     {
+      "name": "binderParcelBenchmark"
+    },
+    {
       "name": "binderTextOutputTest"
     },
     {
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index d960a0b..744da0f 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -105,12 +105,6 @@
     [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
                                              const sp<IBinder>& keepAliveBinder);
 
-    // Start recording transactions to the unique_fd in data.
-    // See RecordedTransaction.h for more details.
-    [[nodiscard]] status_t startRecordingTransactions(const Parcel& data);
-    // Stop the current recording.
-    [[nodiscard]] status_t stopRecordingTransactions();
-
 protected:
     virtual             ~BBinder();
 
@@ -131,6 +125,8 @@
 
     [[nodiscard]] status_t setRpcClientDebug(const Parcel& data);
     void removeRpcServerLink(const sp<RpcServerLink>& link);
+    [[nodiscard]] status_t startRecordingTransactions(const Parcel& data);
+    [[nodiscard]] status_t stopRecordingTransactions();
 
     std::atomic<Extras*> mExtras;
 
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 2e22b84..bda3d19 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -93,7 +93,17 @@
       */
      void reRegister();
 
-   private:
+     /**
+      * Create a second instance of lazy service registrar.
+      *
+      * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar
+      * should be single-instanced, so that the service will only
+      * shut down when all services are unused. A separate instance
+      * is only used to test race conditions.
+      */
+     static LazyServiceRegistrar createExtraTestInstance();
+
+ private:
      std::shared_ptr<internal::ClientCounterCallback> mClientCC;
      LazyServiceRegistrar();
 };
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 162cd40..e28d374 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -34,13 +34,8 @@
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
 
-#ifdef BINDER_IPC_32BIT
-//NOLINTNEXTLINE(google-runtime-int) b/173188702
-typedef unsigned int binder_size_t;
-#else
 //NOLINTNEXTLINE(google-runtime-int) b/173188702
 typedef unsigned long long binder_size_t;
-#endif
 
 struct flat_binder_object;
 
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index ce578e3..81391e9 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -55,7 +55,7 @@
     // For main functions - dangerous for libraries to use
     void startThreadPool();
 
-    bool becomeContextManager();
+    [[nodiscard]] bool becomeContextManager();
 
     sp<IBinder> getStrongProxyForHandle(int32_t handle);
     void expungeHandle(int32_t handle, IBinder* binder);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 1001b64..b804f7b 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -137,7 +137,7 @@
      * used. However, this can be used in order to prevent newer protocol
      * versions from ever being used. This is expected to be useful for testing.
      */
-    void setProtocolVersion(uint32_t version);
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
 
     /**
      * Set the supported transports for sending and receiving file descriptors.
@@ -163,14 +163,18 @@
      * Allows a root object to be created for each session.
      *
      * Takes one argument: a callable that is invoked once per new session.
-     * The callable takes two arguments: a type-erased pointer to an OS- and
-     * transport-specific address structure, e.g., sockaddr_vm for vsock, and
-     * an integer representing the size in bytes of that structure. The
-     * callable should validate the size, then cast the type-erased pointer
-     * to a pointer to the actual type of the address, e.g., const void* to
-     * const sockaddr_vm*.
+     * The callable takes three arguments:
+     * - a weak pointer to the session. If you want to hold onto this in the root object, then
+     *   you should keep a weak pointer, and promote it when needed. For instance, if you refer
+     *   to this from the root object, then you could get ahold of transport-specific information.
+     * - a type-erased pointer to an OS- and transport-specific address structure, e.g.,
+     *   sockaddr_vm for vsock
+     * - an integer representing the size in bytes of that structure. The callable should
+     *   validate the size, then cast the type-erased pointer to a pointer to the actual type of the
+     *   address, e.g., const void* to const sockaddr_vm*.
      */
-    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object);
+    void setPerSessionRootObject(
+            std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object);
     sp<IBinder> getRootObject();
 
     /**
@@ -184,6 +188,13 @@
     void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
 
     /**
+     * Set optional modifier of each newly created server socket.
+     *
+     * The only argument is a successfully created file descriptor, not bound to an address yet.
+     */
+    void setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier);
+
+    /**
      * See RpcTransportCtx::getCertificate
      */
     std::vector<uint8_t> getCertificate(RpcCertificateFormat);
@@ -265,8 +276,9 @@
 
     sp<IBinder> mRootObject;
     wp<IBinder> mRootObjectWeak;
-    std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
+    std::function<sp<IBinder>(wp<RpcSession>, const void*, size_t)> mRootObjectFactory;
     std::function<bool(const void*, size_t)> mConnectionFilter;
+    std::function<void(base::borrowed_fd)> mServerSocketModifier;
     std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
     std::unique_ptr<FdTrigger> mShutdownTrigger;
     RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index fd52a3a..6db9ad9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -39,6 +39,16 @@
 class FdTrigger;
 struct RpcTransportFd;
 
+// for 'friend'
+class RpcTransportRaw;
+class RpcTransportTls;
+class RpcTransportTipcAndroid;
+class RpcTransportTipcTrusty;
+class RpcTransportCtxRaw;
+class RpcTransportCtxTls;
+class RpcTransportCtxTipcAndroid;
+class RpcTransportCtxTipcTrusty;
+
 // Represents a socket connection.
 // No thread-safety is guaranteed for these APIs.
 class RpcTransport {
@@ -92,7 +102,21 @@
      */
     [[nodiscard]] virtual bool isWaiting() = 0;
 
-protected:
+private:
+    // limit the classes which can implement RpcTransport. Being able to change this
+    // interface is important to allow development of RPC binder. In the past, we
+    // changed this interface to use iovec for efficiency, and we added FDs to the
+    // interface. If another transport is needed, it should be added directly here.
+    // non-socket FDs likely also need changes in RpcSession in order to get
+    // connected, and similarly to how addrinfo was type-erased from RPC binder
+    // interfaces when RpcTransportTipc* was added, other changes may be needed
+    // to add more transports.
+
+    friend class ::android::RpcTransportRaw;
+    friend class ::android::RpcTransportTls;
+    friend class ::android::RpcTransportTipcAndroid;
+    friend class ::android::RpcTransportTipcTrusty;
+
     RpcTransport() = default;
 };
 
@@ -117,7 +141,13 @@
     [[nodiscard]] virtual std::vector<uint8_t> getCertificate(
             RpcCertificateFormat format) const = 0;
 
-protected:
+private:
+    // see comment on RpcTransport
+    friend class ::android::RpcTransportCtxRaw;
+    friend class ::android::RpcTransportCtxTls;
+    friend class ::android::RpcTransportCtxTipcAndroid;
+    friend class ::android::RpcTransportCtxTipcTrusty;
+
     RpcTransportCtx() = default;
 };
 
@@ -140,7 +170,7 @@
     RpcTransportCtxFactory() = default;
 };
 
-struct RpcTransportFd {
+struct RpcTransportFd final {
 private:
     mutable bool isPolling{false};
 
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index cefc42f..8c9844c 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -53,7 +53,7 @@
 constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
 constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
 
-constexpr unsigned int kShutdownWaitTime = 11;
+constexpr unsigned int kShutdownWaitTime = 20;
 constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
 
 class MyTestFoo : public IFoo {
@@ -497,14 +497,28 @@
 
 struct DeathRecipientCookie {
     std::function<void(void)>*onDeath, *onUnlink;
+
+    // may contain additional data
+    // - if it contains AIBinder, then you must call AIBinder_unlinkToDeath manually,
+    //   because it would form a strong reference cycle
+    // - if it points to a data member of another structure, this should have a weak
+    //   promotable reference or a strong reference, in case that object is deleted
+    //   while the death recipient is firing
 };
 void LambdaOnDeath(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+
+    // may reference other cookie members
+
     (*funcs->onDeath)();
 };
 void LambdaOnUnlink(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
     (*funcs->onUnlink)();
+
+    // may reference other cookie members
+
+    delete funcs;
 };
 TEST(NdkBinder, DeathRecipient) {
     using namespace std::chrono_literals;
@@ -536,12 +550,12 @@
         unlinkCv.notify_one();
     };
 
-    DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+    DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
 
     AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
     AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
 
-    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
+    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
 
     // the binder driver should return this if the service dies during the transaction
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index d0e35de..b90b40b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1122,6 +1122,10 @@
         }
 
         impl $crate::binder_impl::Deserialize for $enum {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
+
             fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
                 parcel.read().map(Self)
             }
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index f6b09ed..ba26062 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -20,6 +20,7 @@
 use std::error;
 use std::ffi::{CStr, CString};
 use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+use std::ptr;
 use std::result;
 
 pub use sys::binder_status_t as status_t;
@@ -92,7 +93,7 @@
 /// track of and chain binder errors along with service specific errors.
 ///
 /// Used in AIDL transactions to represent failed transactions.
-pub struct Status(*mut sys::AStatus);
+pub struct Status(ptr::NonNull<sys::AStatus>);
 
 // Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
 // duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
@@ -119,7 +120,7 @@
             // Rust takes ownership of the returned pointer.
             sys::AStatus_newOk()
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Create a status object from a service specific error
@@ -147,7 +148,7 @@
                 sys::AStatus_fromServiceSpecificError(err)
             }
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Creates a status object from a service specific error.
@@ -161,7 +162,7 @@
             let ptr = unsafe {
                 sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
             };
-            Self(ptr)
+            Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
         } else {
             exception.into()
         }
@@ -181,7 +182,7 @@
     ///
     /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
     pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Returns `true` if this status represents a successful transaction.
@@ -326,7 +327,7 @@
             // UNKNOWN_ERROR.
             sys::AStatus_fromStatus(status)
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
 
@@ -338,7 +339,7 @@
             // Unknown values will be coerced into EX_TRANSACTION_FAILED.
             sys::AStatus_fromExceptionCode(code as i32)
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
 
@@ -367,7 +368,7 @@
             // pointee, so we need to delete it here. We know that the pointer
             // will be valid here since `Status` always contains a valid pointer
             // while it is alive.
-            sys::AStatus_delete(self.0);
+            sys::AStatus_delete(self.0.as_mut());
         }
     }
 }
@@ -381,11 +382,15 @@
 /// `Status` object is still alive.
 unsafe impl AsNative<sys::AStatus> for Status {
     fn as_native(&self) -> *const sys::AStatus {
-        self.0
+        self.0.as_ptr()
     }
 
     fn as_native_mut(&mut self) -> *mut sys::AStatus {
-        self.0
+        unsafe {
+            // Safety: The pointer will be valid here since `Status` always
+            // contains a valid and initialized pointer while it is alive.
+            self.0.as_mut()
+        }
     }
 }
 
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index de6d649..7fe37f3 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -132,6 +132,14 @@
 }
 
 impl Deserialize for ParcelFileDescriptor {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 4b658fc..5d8c11c 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, FromIBinder, Stability, Strong};
+use crate::binder::{AsNative, FromIBinder, Interface, Stability, Strong};
 use crate::error::{status_result, status_t, Result, Status, StatusCode};
 use crate::parcel::BorrowedParcel;
 use crate::proxy::SpIBinder;
@@ -22,7 +22,7 @@
 
 use std::convert::{TryFrom, TryInto};
 use std::ffi::c_void;
-use std::mem::{self, ManuallyDrop, MaybeUninit};
+use std::mem::{self, ManuallyDrop};
 use std::os::raw::c_char;
 use std::ptr;
 use std::slice;
@@ -60,6 +60,26 @@
 /// A struct whose instances can be restored from a [`Parcel`].
 // Might be able to hook this up as a serde backend in the future?
 pub trait Deserialize: Sized {
+    /// Type for the uninitialized value of this type. Will be either `Self`
+    /// if the type implements `Default`, `Option<Self>` otherwise.
+    type UninitType;
+
+    /// Assert at compile-time that `Self` and `Self::UninitType` have the same
+    /// size and alignment. This will either fail to compile or evaluate to `true`.
+    /// The only two macros that work here are `panic!` and `assert!`, so we cannot
+    /// use `assert_eq!`.
+    const ASSERT_UNINIT_SIZE_AND_ALIGNMENT: bool = {
+        assert!(std::mem::size_of::<Self>() == std::mem::size_of::<Self::UninitType>());
+        assert!(std::mem::align_of::<Self>() == std::mem::align_of::<Self::UninitType>());
+        true
+    };
+
+    /// Return an uninitialized or default-initialized value for this type.
+    fn uninit() -> Self::UninitType;
+
+    /// Convert an initialized value of type `Self` into `Self::UninitType`.
+    fn from_init(value: Self) -> Self::UninitType;
+
     /// Deserialize an instance from the given [`Parcel`].
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
 
@@ -121,7 +141,7 @@
 pub trait DeserializeArray: Deserialize {
     /// Deserialize an array of type from the given parcel.
     fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
-        let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
+        let mut vec: Option<Vec<Self::UninitType>> = None;
         let res = unsafe {
             // Safety: Safe FFI, vec is the correct opaque type expected by
             // allocate_vec and deserialize_element.
@@ -136,8 +156,8 @@
         let vec: Option<Vec<Self>> = unsafe {
             // Safety: We are assuming that the NDK correctly initialized every
             // element of the vector by now, so we know that all the
-            // MaybeUninits are now properly initialized. We can transmute from
-            // Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> has the same
+            // UninitTypes are now properly initialized. We can transmute from
+            // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
             // alignment and size as T, so the pointer to the vector allocation
             // will be compatible.
             mem::transmute(vec)
@@ -149,14 +169,14 @@
 /// Callback to deserialize a parcelable element.
 ///
 /// The opaque array data pointer must be a mutable pointer to an
-/// `Option<Vec<MaybeUninit<T>>>` with at least enough elements for `index` to be valid
+/// `Option<Vec<T::UninitType>>` with at least enough elements for `index` to be valid
 /// (zero-based).
 unsafe extern "C" fn deserialize_element<T: Deserialize>(
     parcel: *const sys::AParcel,
     array: *mut c_void,
     index: usize,
 ) -> status_t {
-    let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>);
+    let vec = &mut *(array as *mut Option<Vec<T::UninitType>>);
     let vec = match vec {
         Some(v) => v,
         None => return StatusCode::BAD_INDEX as status_t,
@@ -170,7 +190,7 @@
         Ok(e) => e,
         Err(code) => return code as status_t,
     };
-    ptr::write(vec[index].as_mut_ptr(), element);
+    vec[index] = T::from_init(element);
     StatusCode::OK as status_t
 }
 
@@ -233,15 +253,15 @@
 /// # Safety
 ///
 /// The opaque data pointer passed to the array read function must be a mutable
-/// pointer to an `Option<Vec<MaybeUninit<T>>>`. `buffer` will be assigned a mutable pointer
+/// pointer to an `Option<Vec<T::UninitType>>`. `buffer` will be assigned a mutable pointer
 /// to the allocated vector data if this function returns true.
-unsafe extern "C" fn allocate_vec_with_buffer<T>(
+unsafe extern "C" fn allocate_vec_with_buffer<T: Deserialize>(
     data: *mut c_void,
     len: i32,
     buffer: *mut *mut T,
 ) -> bool {
     let res = allocate_vec::<T>(data, len);
-    let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
+    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
     if let Some(new_vec) = vec {
         *buffer = new_vec.as_mut_ptr() as *mut T;
     }
@@ -253,20 +273,18 @@
 /// # Safety
 ///
 /// The opaque data pointer passed to the array read function must be a mutable
-/// pointer to an `Option<Vec<MaybeUninit<T>>>`.
-unsafe extern "C" fn allocate_vec<T>(data: *mut c_void, len: i32) -> bool {
-    let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
+/// pointer to an `Option<Vec<T::UninitType>>`.
+unsafe extern "C" fn allocate_vec<T: Deserialize>(data: *mut c_void, len: i32) -> bool {
+    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
     if len < 0 {
         *vec = None;
         return true;
     }
-    let mut new_vec: Vec<MaybeUninit<T>> = Vec::with_capacity(len as usize);
 
-    // Safety: We are filling the vector with uninitialized data here, but this
-    // is safe because the vector contains MaybeUninit elements which can be
-    // uninitialized. We're putting off the actual unsafe bit, transmuting the
-    // vector to a Vec<T> until the contents are initialized.
-    new_vec.set_len(len as usize);
+    // Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
+    let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
+    let mut new_vec: Vec<T::UninitType> = Vec::with_capacity(len as usize);
+    new_vec.resize_with(len as usize, T::uninit);
 
     ptr::write(vec, Some(new_vec));
     true
@@ -283,8 +301,11 @@
 }
 
 /// Safety: All elements in the vector must be properly initialized.
-unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> {
-    // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T>
+unsafe fn vec_assume_init<T: Deserialize>(vec: Vec<T::UninitType>) -> Vec<T> {
+    // Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
+    let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
+
+    // We can convert from Vec<T::UninitType> to Vec<T> because T::UninitType
     // has the same alignment and size as T, so the pointer to the vector
     // allocation will be compatible.
     let mut vec = ManuallyDrop::new(vec);
@@ -307,6 +328,9 @@
 
     {Deserialize, $ty:ty, $read_fn:path} => {
         impl Deserialize for $ty {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
             fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
                 let mut val = Self::default();
                 unsafe {
@@ -348,11 +372,11 @@
     {DeserializeArray, $ty:ty, $read_array_fn:path} => {
         impl DeserializeArray for $ty {
             fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
-                let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
+                let mut vec: Option<Vec<Self::UninitType>> = None;
                 let status = unsafe {
                     // Safety: `Parcel` always contains a valid pointer to an
                     // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
-                    // be of type `*mut Option<Vec<MaybeUninit<T>>>`, so `&mut vec` is
+                    // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
                     // correct for it.
                     $read_array_fn(
                         parcel.as_native(),
@@ -364,7 +388,7 @@
                 let vec: Option<Vec<Self>> = unsafe {
                     // Safety: We are assuming that the NDK correctly
                     // initialized every element of the vector by now, so we
-                    // know that all the MaybeUninits are now properly
+                    // know that all the UninitTypes are now properly
                     // initialized.
                     vec.map(|vec| vec_assume_init(vec))
                 };
@@ -440,6 +464,14 @@
 }
 
 impl Deserialize for u8 {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         i8::deserialize(parcel).map(|v| v as u8)
     }
@@ -471,6 +503,14 @@
 }
 
 impl Deserialize for i16 {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         u16::deserialize(parcel).map(|v| v as i16)
     }
@@ -547,6 +587,14 @@
 }
 
 impl Deserialize for Option<String> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut vec: Option<Vec<u8>> = None;
         let status = unsafe {
@@ -575,6 +623,14 @@
 impl DeserializeArray for Option<String> {}
 
 impl Deserialize for String {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
@@ -611,6 +667,14 @@
 }
 
 impl<T: DeserializeArray> Deserialize for Vec<T> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         DeserializeArray::deserialize_array(parcel)
             .transpose()
@@ -640,6 +704,14 @@
 impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {}
 
 impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] {
+    type UninitType = [T::UninitType; N];
+    fn uninit() -> Self::UninitType {
+        [(); N].map(|_| T::uninit())
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value.map(T::from_init)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let vec = DeserializeArray::deserialize_array(parcel)
             .transpose()
@@ -664,6 +736,14 @@
 }
 
 impl Deserialize for Stability {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         i32::deserialize(parcel).and_then(Stability::try_from)
     }
@@ -682,6 +762,14 @@
 }
 
 impl Deserialize for Status {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut status_ptr = ptr::null_mut();
         let ret_status = unsafe {
@@ -717,12 +805,29 @@
 impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
 
 impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
+    type UninitType = Option<Strong<T>>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let ibinder: SpIBinder = parcel.read()?;
         FromIBinder::try_from(ibinder)
     }
 }
 
+struct AssertIBinder;
+impl Interface for AssertIBinder {}
+impl FromIBinder for AssertIBinder {
+    // This is only needed so we can assert on the size of Strong<AssertIBinder>
+    fn try_from(_: SpIBinder) -> Result<Strong<Self>> {
+        unimplemented!()
+    }
+}
+
 impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
     fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
         let ibinder: Option<SpIBinder> = parcel.read()?;
@@ -752,6 +857,14 @@
 }
 
 impl<T: DeserializeOption> Deserialize for Option<T> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         DeserializeOption::deserialize_option(parcel)
     }
@@ -821,6 +934,9 @@
     };
     ($parcelable:ident < $( $param:ident ),* > ) => {
         impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
             fn deserialize(
                 parcel: &$crate::binder_impl::BorrowedParcel<'_>,
             ) -> std::result::Result<Self, $crate::StatusCode> {
@@ -876,6 +992,14 @@
 }
 
 impl<T: Deserialize> Deserialize for Box<T> {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).map(Box::new)
     }
@@ -900,6 +1024,7 @@
 
     #[test]
     fn test_custom_parcelable() {
+        #[derive(Default)]
         struct Custom(u32, bool, String, Vec<String>);
 
         impl Serialize for Custom {
@@ -912,6 +1037,14 @@
         }
 
         impl Deserialize for Custom {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType {
+                Self::UninitType::default()
+            }
+            fn from_init(value: Self) -> Self::UninitType {
+                value
+            }
+
             fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
                 Ok(Custom(
                     parcel.read()?,
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index c829d37..383cc83 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -169,6 +169,14 @@
 }
 
 impl Deserialize for ParcelableHolder {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::new(Default::default())
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
         let status: i32 = parcel.read()?;
         if status == NULL_PARCELABLE_FLAG {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 254efae..036f6b4 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -439,6 +439,14 @@
 impl SerializeArray for SpIBinder {}
 
 impl Deserialize for SpIBinder {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> {
         parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index df8a2af..ac96823 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -21,6 +21,7 @@
             "waghpawan@google.com",
             "smoreland@google.com",
         ],
+        triage_assignee: "waghpawan@google.com",
         // hotlist "AIDL fuzzers bugs" on buganizer
         hotlists: ["4637097"],
     },
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 5cb406a..89126ca 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -19,6 +19,11 @@
     srcs: [
         "service_fuzzer.rs",
     ],
+    shared_libs: [
+        "libbinder",
+        "libbinder_ndk",
+        "libutils",
+    ],
     rustlibs: [
         "libbinder_rs",
         "libbinder_random_parcel_rs",
@@ -29,6 +34,7 @@
             "waghpawan@google.com",
             "smoreland@google.com",
         ],
+        triage_assignee: "waghpawan@google.com",
         // hotlist "AIDL fuzzers bugs" on buganizer
         hotlists: ["4637097"],
     },
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 873e955..41856f9 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -32,28 +32,8 @@
 }
 
 cc_test {
-    name: "binderDriverInterfaceTest_IPC_32",
-    defaults: ["binder_test_defaults"],
-    srcs: ["binderDriverInterfaceTest.cpp"],
-    header_libs: ["libbinder_headers"],
-    compile_multilib: "32",
-    multilib: {
-        lib32: {
-            suffix: "",
-        },
-    },
-    cflags: ["-DBINDER_IPC_32BIT=1"],
-    test_suites: ["vts"],
-}
-
-cc_test {
     name: "binderDriverInterfaceTest",
     defaults: ["binder_test_defaults"],
-    product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
-        },
-    },
     header_libs: ["libbinder_headers"],
     srcs: ["binderDriverInterfaceTest.cpp"],
     test_suites: [
@@ -62,30 +42,6 @@
     ],
 }
 
-cc_test {
-    name: "binderLibTest_IPC_32",
-    defaults: ["binder_test_defaults"],
-    srcs: ["binderLibTest.cpp"],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libutils",
-    ],
-    static_libs: [
-        "libgmock",
-    ],
-    compile_multilib: "32",
-    multilib: {
-        lib32: {
-            suffix: "",
-        },
-    },
-    cflags: ["-DBINDER_IPC_32BIT=1"],
-    test_suites: ["vts"],
-    require_root: true,
-}
-
 // unit test only, which can run on host and doesn't use /dev/binder
 cc_test {
     name: "binderUnitTest",
@@ -111,13 +67,39 @@
 }
 
 cc_test {
-    name: "binderLibTest",
-    defaults: ["binder_test_defaults"],
-    product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
+    name: "binderRecordReplayTest",
+    srcs: ["binderRecordReplayTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+    static_libs: [
+        "binderRecordReplayTestIface-cpp",
+        "binderReadParcelIface-cpp",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
+aidl_interface {
+    name: "binderRecordReplayTestIface",
+    unstable: true,
+    srcs: [
+        "IBinderRecordReplayTest.aidl",
+    ],
+    imports: ["binderReadParcelIface"],
+    backend: {
+        java: {
+            enabled: true,
+            platform_apis: true,
         },
     },
+}
+
+cc_test {
+    name: "binderLibTest",
+    defaults: ["binder_test_defaults"],
 
     srcs: ["binderLibTest.cpp"],
     shared_libs: [
@@ -716,6 +698,7 @@
         "liblog",
         "libutils",
     ],
+    test_suites: ["general-tests"],
 }
 
 cc_test_host {
@@ -818,3 +801,15 @@
         hotlists: ["4637097"],
     },
 }
+
+cc_defaults {
+    name: "fuzzer_disable_leaks",
+    fuzz_config: {
+        asan_options: [
+            "detect_leaks=0",
+        ],
+        hwasan_options: [
+            "detect_leaks=0",
+        ],
+    },
+}
diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl
new file mode 100644
index 0000000..bd6b03c
--- /dev/null
+++ b/libs/binder/tests/IBinderRecordReplayTest.aidl
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+import parcelables.SingleDataParcelable;
+
+interface IBinderRecordReplayTest {
+    void setByte(byte input);
+    byte getByte();
+
+    void setChar(char input);
+    char getChar();
+
+    void setBoolean(boolean input);
+    boolean getBoolean();
+
+    void setInt(int input);
+    int getInt();
+
+    void setFloat(float input);
+    float getFloat();
+
+    void setLong(long input);
+    long getLong();
+
+    void setDouble(double input);
+    double getDouble();
+
+    void setString(String input);
+    String getString();
+
+    void setSingleDataParcelable(in SingleDataParcelable p);
+    SingleDataParcelable getSingleDataParcelable();
+
+    void setByteArray(in byte[] input);
+    byte[] getByteArray();
+
+    void setCharArray(in char[] input);
+    char[] getCharArray();
+
+    void setBooleanArray(in boolean[] input);
+    boolean[] getBooleanArray();
+
+    void setIntArray(in int[] input);
+    int[] getIntArray();
+
+    void setFloatArray(in float[] input);
+    float[] getFloatArray();
+
+    void setLongArray(in long[] input);
+    long[] getLongArray();
+
+    void setDoubleArray(in double[] input);
+    double[] getDoubleArray();
+
+    void setStringArray(in String[] input);
+    String[] getStringArray();
+
+    void setSingleDataParcelableArray(in SingleDataParcelable[] input);
+    SingleDataParcelable[] getSingleDataParcelableArray();
+}
diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h
deleted file mode 100644
index 369b55d..0000000
--- a/libs/binder/tests/binderAbiHelper.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdlib.h>
-#include <iostream>
-
-#ifdef BINDER_IPC_32BIT
-static constexpr bool kBuild32Abi = true;
-#else
-static constexpr bool kBuild32Abi = false;
-#endif
-
-// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported
-static inline bool ReadKernelConfigIs32BitAbi() {
-    // failure case implies we run with standard ABI
-    return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\"");
-}
-
-static inline void ExitIfWrongAbi() {
-    bool runtime32Abi = ReadKernelConfigIs32BitAbi();
-
-    if (kBuild32Abi != runtime32Abi) {
-        std::cout << "[==========] Running 1 test from 1 test suite." << std::endl;
-        std::cout << "[----------] Global test environment set-up." << std::endl;
-        std::cout << "[----------] 1 tests from BinderLibTest" << std::endl;
-        std::cout << "[ RUN      ] BinderTest.AbortForWrongAbi" << std::endl;
-        std::cout << "[ INFO     ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl;
-        std::cout << "[       OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl;
-        std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl;
-        std::cout << "" << std::endl;
-        std::cout << "[----------] Global test environment tear-down" << std::endl;
-        std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl;
-        std::cout << "[  PASSED  ] 1 tests." << std::endl;
-        exit(0);
-    }
-}
-
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 8cc3054..cf23a46 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -25,8 +25,6 @@
 #include <sys/mman.h>
 #include <poll.h>
 
-#include "binderAbiHelper.h"
-
 #define BINDER_DEV_NAME "/dev/binder"
 
 testing::Environment* binder_env;
@@ -362,8 +360,7 @@
     binderTestReadEmpty();
 }
 
-int main(int argc, char **argv) {
-    ExitIfWrongAbi();
+int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
 
     binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 8974ad7..abc423b 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -48,7 +48,6 @@
 #include <sys/un.h>
 
 #include "../binder_module.h"
-#include "binderAbiHelper.h"
 
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
@@ -2022,9 +2021,7 @@
     return 1; /* joinThreadPool should not return */
 }
 
-int main(int argc, char **argv) {
-    ExitIfWrongAbi();
-
+int main(int argc, char** argv) {
     if (argc == 4 && !strcmp(argv[1], "--servername")) {
         binderservername = argv[2];
     } else {
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
new file mode 100644
index 0000000..17d5c8a
--- /dev/null
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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 <BnBinderRecordReplayTest.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/RecordedTransaction.h>
+#include <gtest/gtest.h>
+
+#include <sys/prctl.h>
+
+#include "parcelables/SingleDataParcelable.h"
+
+using namespace android;
+using android::binder::Status;
+using android::binder::debug::RecordedTransaction;
+using parcelables::SingleDataParcelable;
+
+const String16 kServerName = String16("binderRecordReplay");
+
+#define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \
+    Status set##name(T input) {                   \
+        m##name = input;                          \
+        return Status::ok();                      \
+    }                                             \
+                                                  \
+    Status get##name(T* output) {                 \
+        *output = m##name;                        \
+        return Status::ok();                      \
+    }                                             \
+    T m##name
+
+#define GENERATE_GETTER_SETTER(name, T) \
+    Status set##name(const T& input) {  \
+        m##name = input;                \
+        return Status::ok();            \
+    }                                   \
+                                        \
+    Status get##name(T* output) {       \
+        *output = m##name;              \
+        return Status::ok();            \
+    }                                   \
+    T m##name
+
+class MyRecordReplay : public BnBinderRecordReplayTest {
+public:
+    GENERATE_GETTER_SETTER_PRIMITIVE(Boolean, bool);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Byte, int8_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Int, int);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Char, char16_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Long, int64_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Float, float);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Double, double);
+
+    GENERATE_GETTER_SETTER(String, String16);
+    GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable);
+
+    GENERATE_GETTER_SETTER(BooleanArray, std::vector<bool>);
+    GENERATE_GETTER_SETTER(ByteArray, std::vector<uint8_t>);
+    GENERATE_GETTER_SETTER(IntArray, std::vector<int>);
+    GENERATE_GETTER_SETTER(CharArray, std::vector<char16_t>);
+    GENERATE_GETTER_SETTER(LongArray, std::vector<int64_t>);
+    GENERATE_GETTER_SETTER(FloatArray, std::vector<float>);
+    GENERATE_GETTER_SETTER(DoubleArray, std::vector<double>);
+    GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>);
+    GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
+};
+
+class BinderRecordReplayTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        // get the remote service
+        auto binder = defaultServiceManager()->getService(kServerName);
+        ASSERT_NE(nullptr, binder);
+        mInterface = interface_cast<IBinderRecordReplayTest>(binder);
+        mBpBinder = binder->remoteBinder();
+        ASSERT_NE(nullptr, mBpBinder);
+    }
+
+    template <typename T, typename U>
+    void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue,
+                      Status (IBinderRecordReplayTest::*get)(U*), U changedValue) {
+        base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
+                                O_RDWR | O_CREAT | O_CLOEXEC, 0666));
+        ASSERT_TRUE(fd.ok());
+
+        // record a transaction
+        mBpBinder->startRecordingBinder(fd);
+        auto status = (*mInterface.*set)(recordedValue);
+        EXPECT_TRUE(status.isOk());
+        mBpBinder->stopRecordingBinder();
+
+        // test transaction does the thing we expect it to do
+        U output;
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(output, recordedValue);
+
+        // write over the existing state
+        status = (*mInterface.*set)(changedValue);
+        EXPECT_TRUE(status.isOk());
+
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+
+        EXPECT_EQ(output, changedValue);
+
+        // replay transaction
+        ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
+        std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
+        ASSERT_NE(transaction, std::nullopt);
+
+        // TODO: move logic to replay RecordedTransaction into RecordedTransaction
+        Parcel data;
+        data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
+        auto result =
+                mBpBinder->transact(transaction->getCode(), data, nullptr, transaction->getFlags());
+
+        // make sure recording does the thing we expect it to do
+        EXPECT_EQ(OK, result);
+
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(output, recordedValue);
+    }
+
+private:
+    sp<BpBinder> mBpBinder;
+    sp<IBinderRecordReplayTest> mInterface;
+};
+
+TEST_F(BinderRecordReplayTest, ReplayByte) {
+    recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte,
+                 int8_t{90});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayBoolean) {
+    recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean,
+                 false);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayChar) {
+    recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'},
+                 &IBinderRecordReplayTest::getChar, char16_t{'K'});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayInt) {
+    recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayFloat) {
+    recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat,
+                 22.0f);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayLong) {
+    recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55},
+                 &IBinderRecordReplayTest::getLong, int64_t{1LL << 12});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayDouble) {
+    recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble,
+                 1.11);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayString) {
+    const ::android::String16& input1 = String16("This is saved string");
+    const ::android::String16& input2 = String16("This is changed string");
+    recordReplay(&IBinderRecordReplayTest::setString, input1, &IBinderRecordReplayTest::getString,
+                 input2);
+}
+
+TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelable) {
+    SingleDataParcelable saved, changed;
+    saved.data = 3;
+    changed.data = 5;
+    recordReplay(&IBinderRecordReplayTest::setSingleDataParcelable, saved,
+                 &IBinderRecordReplayTest::getSingleDataParcelable, changed);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayByteArray) {
+    std::vector<uint8_t> savedArray = {uint8_t{255}, uint8_t{0}, uint8_t{127}};
+    std::vector<uint8_t> changedArray = {uint8_t{2}, uint8_t{7}, uint8_t{117}};
+    recordReplay(&IBinderRecordReplayTest::setByteArray, savedArray,
+                 &IBinderRecordReplayTest::getByteArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayBooleanArray) {
+    std::vector<bool> savedArray = {true, false, true};
+    std::vector<bool> changedArray = {false, true, false};
+    recordReplay(&IBinderRecordReplayTest::setBooleanArray, savedArray,
+                 &IBinderRecordReplayTest::getBooleanArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayCharArray) {
+    std::vector<char16_t> savedArray = {char16_t{'G'}, char16_t{'L'}, char16_t{'K'}, char16_t{'T'}};
+    std::vector<char16_t> changedArray = {char16_t{'X'}, char16_t{'Y'}, char16_t{'Z'}};
+    recordReplay(&IBinderRecordReplayTest::setCharArray, savedArray,
+                 &IBinderRecordReplayTest::getCharArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayIntArray) {
+    std::vector<int> savedArray = {12, 45, 178};
+    std::vector<int> changedArray = {32, 14, 78, 1899};
+    recordReplay(&IBinderRecordReplayTest::setIntArray, savedArray,
+                 &IBinderRecordReplayTest::getIntArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayFloatArray) {
+    std::vector<float> savedArray = {12.14f, 45.56f, 123.178f};
+    std::vector<float> changedArray = {0.00f, 14.0f, 718.1f, 1899.122f, 3268.123f};
+    recordReplay(&IBinderRecordReplayTest::setFloatArray, savedArray,
+                 &IBinderRecordReplayTest::getFloatArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayLongArray) {
+    std::vector<int64_t> savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}};
+    std::vector<int64_t> changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33},
+                                         int64_t{1LL << 62}};
+    recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray,
+                 &IBinderRecordReplayTest::getLongArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayDoubleArray) {
+    std::vector<double> savedArray = {12.1412313, 45.561232, 123.1781111};
+    std::vector<double> changedArray = {0.00111, 14.32130, 712312318.19, 1899212.122,
+                                        322168.122123};
+    recordReplay(&IBinderRecordReplayTest::setDoubleArray, savedArray,
+                 &IBinderRecordReplayTest::getDoubleArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayStringArray) {
+    std::vector<String16> savedArray = {String16("This is saved value"), String16(),
+                                        String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")};
+
+    std::vector<String16> changedArray = {String16("This is changed value"),
+                                          String16("\xF0\x90\x90\xB7\xE2\x82\xAC")};
+    recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray,
+                 &IBinderRecordReplayTest::getStringArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) {
+    SingleDataParcelable s1, s2, s3, s4, s5;
+    s1.data = 5213;
+    s2.data = 1512;
+    s3.data = 4233;
+    s4.data = 123124;
+    s5.data = 0;
+    std::vector<SingleDataParcelable> saved = {s1, s2, s3};
+    std::vector<SingleDataParcelable> changed = {s4, s5};
+
+    recordReplay(&IBinderRecordReplayTest::setSingleDataParcelableArray, saved,
+                 &IBinderRecordReplayTest::getSingleDataParcelableArray, changed);
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+        auto server = sp<MyRecordReplay>::make();
+        android::defaultServiceManager()->addService(kServerName, server.get());
+
+        IPCThreadState::self()->joinThreadPool(true);
+        exit(1); // should not reach
+    }
+
+    // not racey, but getService sleeps for 1s
+    usleep(100000);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 5939273..9c96c41 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -129,12 +129,33 @@
     }
 }
 
+static void SetLabel(benchmark::State& state) {
+    Transport transport = static_cast<Transport>(state.range(0));
+    switch (transport) {
+#ifdef __BIONIC__
+        case KERNEL:
+            state.SetLabel("kernel");
+            break;
+#endif
+        case RPC:
+            state.SetLabel("rpc");
+            break;
+        case RPC_TLS:
+            state.SetLabel("rpc_tls");
+            break;
+        default:
+            LOG(FATAL) << "Unknown transport value: " << transport;
+    }
+}
+
 void BM_pingTransaction(benchmark::State& state) {
     sp<IBinder> binder = getBinderForOptions(state);
 
     while (state.KeepRunning()) {
         CHECK_EQ(OK, binder->pingBinder());
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_pingTransaction)->ArgsProduct({kTransportList});
 
@@ -164,6 +185,8 @@
         Status ret = iface->repeatString(str, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_repeatTwoPageString)->ArgsProduct({kTransportList});
 
@@ -182,6 +205,8 @@
         Status ret = iface->repeatBytes(bytes, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_throughputForTransportAndBytes)
         ->ArgsProduct({kTransportList,
@@ -201,6 +226,8 @@
         Status ret = iface->repeatBinder(binder, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList});
 
@@ -228,11 +255,6 @@
     ::benchmark::Initialize(&argc, argv);
     if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
 
-    std::cerr << "Tests suffixes:" << std::endl;
-    std::cerr << "\t.../" << Transport::KERNEL << " is KERNEL" << std::endl;
-    std::cerr << "\t.../" << Transport::RPC << " is RPC" << std::endl;
-    std::cerr << "\t.../" << Transport::RPC_TLS << " is RPC with TLS" << std::endl;
-
 #ifdef __BIONIC__
     if (0 == fork()) {
         prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 8d13007..505f30f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -462,7 +462,7 @@
     EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
 
     // Potential flake, but make sure calls are handled in parallel.
-    EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
+    EXPECT_LE(epochMsAfter, epochMsBefore + 4 * sleepMs);
 }
 
 TEST_P(BinderRpc, ThreadPoolOverSaturated) {
@@ -687,6 +687,8 @@
     }
 
     EXPECT_EQ(nullptr, session.promote());
+
+    sleep(1); // give time for remote session to shutdown
 }
 
 TEST_P(BinderRpc, SingleDeathRecipient) {
@@ -1353,7 +1355,7 @@
     base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     int sinkFd = sink.get();
     auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_FALSE(server->hasServer());
     ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
     ASSERT_TRUE(server->hasServer());
@@ -1369,7 +1371,7 @@
 
     auto addr = allocateSocketAddress();
     auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
     auto joinEnds = std::make_shared<OneOffSignal>();
 
@@ -1418,7 +1420,9 @@
                 std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
             auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
             auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
-            rpcServer->setProtocolVersion(serverVersion);
+            if (!rpcServer->setProtocolVersion(serverVersion)) {
+                return AssertionFailure() << "Invalid protocol version: " << serverVersion;
+            }
             switch (socketType) {
                 case SocketType::PRECONNECTED: {
                     return AssertionFailure() << "Not supported by this test";
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index a9736d5..cb09a7f 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -118,7 +118,7 @@
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
     sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
 
-    server->setProtocolVersion(serverConfig.serverVersion);
+    CHECK(server->setProtocolVersion(serverConfig.serverVersion));
     server->setMaxThreads(serverConfig.numThreads);
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
@@ -164,7 +164,12 @@
         }
     }
 
-    server->setPerSessionRootObject([&](const void* addrPtr, size_t len) {
+    server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) {
+        {
+            sp<RpcSession> spSession = session.promote();
+            CHECK_NE(nullptr, spSession.get());
+        }
+
         // UNIX sockets with abstract addresses return
         // sizeof(sa_family_t)==2 in addrlen
         CHECK_GE(len, sizeof(sa_family_t));
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index 8557389..cb632e9 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -90,15 +90,18 @@
 
         auto server = std::move(*serverOrErr);
         serverInfo.server = server;
-        serverInfo.server->setProtocolVersion(serverVersion);
-        serverInfo.server->setPerSessionRootObject([=](const void* /*addrPtr*/, size_t /*len*/) {
-            auto service = sp<MyBinderRpcTestTrusty>::make();
-            // Assign a unique connection identifier to service->port so
-            // getClientPort returns a unique value per connection
-            service->port = ++gConnectionCounter;
-            service->server = server;
-            return service;
-        });
+        if (!serverInfo.server->setProtocolVersion(serverVersion)) {
+            return EXIT_FAILURE;
+        }
+        serverInfo.server->setPerSessionRootObject(
+                [=](wp<RpcSession> /*session*/, const void* /*addrPtr*/, size_t /*len*/) {
+                    auto service = sp<MyBinderRpcTestTrusty>::make();
+                    // Assign a unique connection identifier to service->port so
+                    // getClientPort returns a unique value per connection
+                    service->port = ++gConnectionCounter;
+                    service->server = server;
+                    return service;
+                });
 
         servers.push_back(std::move(serverInfo));
     }
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index c857d62..5e8a32a 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -35,6 +35,7 @@
 
 #include <optional>
 
+#include <inttypes.h>
 #include <sys/eventfd.h>
 #include <sys/prctl.h>
 
@@ -686,10 +687,12 @@
     // Determine the maximum number of fds this process can have open
     struct rlimit limit {};
     ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit));
-    uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur);
+    uint64_t maxFds = limit.rlim_cur;
+
+    ALOG(LOG_INFO, "SafeInterfaceTest", "%s max FDs: %" PRIu64, __PRETTY_FUNCTION__, maxFds);
 
     // Perform this test enough times to rule out fd leaks
-    for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) {
+    for (uint32_t iter = 0; iter < (maxFds + 100); ++iter) {
         native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
         ASSERT_NE(nullptr, handle);
         handle->data[0] = dup(eventFd.get());
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
index a9a6197..cb37cfa 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
@@ -19,7 +19,17 @@
 #include <binder/IBinder.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
@@ -34,4 +44,5 @@
  *   }
  */
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider);
+
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
index f2b7823..d8bf87a 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
@@ -16,10 +16,21 @@
 
 #pragma once
 
+#include <android/binder_auto_utils.h>
 #include <android/binder_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 8bef33f..69f7147 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -24,10 +24,12 @@
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
-    sp<IBinder> target;
+    fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
+}
 
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
     RandomParcelOptions options{
-            .extraBinders = {binder},
+            .extraBinders = binders,
             .extraFds = {},
     };
 
@@ -37,47 +39,73 @@
     }
 
     while (provider.remaining_bytes() > 0) {
-        // Most of the AIDL services will have small set of transaction codes.
-        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
-                                               : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
-        uint32_t flags = provider.ConsumeIntegral<uint32_t>();
-        Parcel data;
-        // for increased fuzz coverage
-        data.setEnforceNoDataAvail(provider.ConsumeBool());
+        provider.PickValueInArray<std::function<void()>>({
+                [&]() {
+                    // Most of the AIDL services will have small set of transaction codes.
+                    uint32_t code = provider.ConsumeBool()
+                            ? provider.ConsumeIntegral<uint32_t>()
+                            : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+                    uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+                    Parcel data;
+                    // for increased fuzz coverage
+                    data.setEnforceNoDataAvail(provider.ConsumeBool());
 
-        sp<IBinder> target = options.extraBinders.at(
-                provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
-        options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
-            // most code will be behind checks that the head of the Parcel
-            // is exactly this, so make it easier for fuzzers to reach this
-            if (provider.ConsumeBool()) {
-                p->writeInterfaceToken(target->getInterfaceDescriptor());
-            }
-        };
+                    sp<IBinder> target = options.extraBinders.at(
+                            provider.ConsumeIntegralInRange<size_t>(0,
+                                                                    options.extraBinders.size() -
+                                                                            1));
+                    options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
+                        // most code will be behind checks that the head of the Parcel
+                        // is exactly this, so make it easier for fuzzers to reach this
+                        if (provider.ConsumeBool()) {
+                            p->writeInterfaceToken(target->getInterfaceDescriptor());
+                        }
+                    };
 
-        std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
-                provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
+                    std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
+                            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+                    fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()),
+                                     &options);
 
-        Parcel reply;
-        // for increased fuzz coverage
-        reply.setEnforceNoDataAvail(provider.ConsumeBool());
-        (void)target->transact(code, data, &reply, flags);
+                    Parcel reply;
+                    // for increased fuzz coverage
+                    reply.setEnforceNoDataAvail(provider.ConsumeBool());
+                    (void)target->transact(code, data, &reply, flags);
 
-        // feed back in binders and fds that are returned from the service, so that
-        // we can fuzz those binders, and use the fds and binders to feed back into
-        // the binders
-        auto retBinders = reply.debugReadAllStrongBinders();
-        options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
-                                    retBinders.end());
-        auto retFds = reply.debugReadAllFileDescriptors();
-        for (size_t i = 0; i < retFds.size(); i++) {
-            options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
-        }
+                    // feed back in binders and fds that are returned from the service, so that
+                    // we can fuzz those binders, and use the fds and binders to feed back into
+                    // the binders
+                    auto retBinders = reply.debugReadAllStrongBinders();
+                    options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
+                                                retBinders.end());
+                    auto retFds = reply.debugReadAllFileDescriptors();
+                    for (size_t i = 0; i < retFds.size(); i++) {
+                        options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
+                    }
+                },
+                [&]() {
+                    if (options.extraFds.size() == 0) {
+                        return;
+                    }
+                    uint32_t toDelete =
+                            provider.ConsumeIntegralInRange<uint32_t>(0,
+                                                                      options.extraFds.size() - 1);
+                    options.extraFds.erase(options.extraFds.begin() + toDelete);
+                },
+                [&]() {
+                    if (options.extraBinders.size() <= 1) {
+                        return;
+                    }
+                    uint32_t toDelete =
+                            provider.ConsumeIntegralInRange<uint32_t>(0,
+                                                                      options.extraBinders.size() -
+                                                                              1);
+                    options.extraBinders.erase(options.extraBinders.begin() + toDelete);
+                },
+        })();
     }
 
     // invariants
-
     auto ps = ProcessState::selfOrNull();
     if (ps) {
         CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount())
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
index a1fb701..0b0ca34 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
@@ -24,6 +24,15 @@
 
 namespace android {
 
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider) {
+    std::vector<sp<IBinder>> cppBinders;
+    for (const auto& binder : binders) {
+        cppBinders.push_back(binder.get()->getBinder());
+    }
+
+    fuzzService(cppBinders, std::move(provider));
+}
+
 void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) {
     fuzzService(binder->getBinder(), std::move(provider));
 }
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
new file mode 100644
index 0000000..e60ca22
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -0,0 +1,55 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+aidl_interface {
+    name: "testServiceIface",
+    host_supported: true,
+    unstable: true,
+    srcs: [
+        "ITestService.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: true,
+            platform_apis: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
+// Adding this fuzzer to test the fuzzService functionality
+cc_fuzz {
+    name: "test_service_fuzzer_should_crash",
+    defaults: [
+        "service_fuzzer_defaults",
+    ],
+    static_libs: [
+        "liblog",
+        "testServiceIface-cpp",
+    ],
+    host_supported: true,
+    srcs: ["TestServiceFuzzer.cpp"],
+    fuzz_config: {
+        triage_assignee: "waghpawan@google.com",
+
+        // This fuzzer should be used only test fuzzService locally
+        fuzz_on_haiku_host: false,
+        fuzz_on_haiku_device: false,
+    },
+}
+
+sh_test_host {
+    name: "fuzz_service_test",
+    src: "run_fuzz_service_test.sh",
+    filename: "run_fuzz_service_test.sh",
+    test_config: "fuzz_service_test_config.xml",
+    data_bins: [
+        "test_service_fuzzer_should_crash",
+    ],
+    required: [
+        "test_service_fuzzer_should_crash",
+    ],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
new file mode 100644
index 0000000..3eadc02
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+interface ITestService {
+
+    void setIntData(int input);
+
+    void setCharData(char input);
+
+    void setBooleanData(boolean input);
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
new file mode 100644
index 0000000..8907ea0
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 <BnTestService.h>
+#include <fuzzbinder/libbinder_driver.h>
+
+#include <log/log.h>
+
+using android::fuzzService;
+using android::sp;
+using android::binder::Status;
+
+namespace android {
+// This service is to verify that fuzzService is functioning properly
+class TestService : public BnTestService {
+public:
+    Status setIntData(int /*input*/) {
+        LOG_ALWAYS_FATAL("Expected crash in setIntData");
+        return Status::ok();
+    }
+
+    Status setCharData(char16_t /*input*/) {
+        LOG_ALWAYS_FATAL("Expected crash in setCharData");
+        return Status::ok();
+    }
+
+    Status setBooleanData(bool /*input*/) {
+        LOG_ALWAYS_FATAL("Expected crash in setBooleanData");
+        return Status::ok();
+    }
+};
+} // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto service = sp<android::TestService>::make();
+    fuzzService(service, FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
new file mode 100644
index 0000000..19eb33a
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs fuzzService test">
+    <option name="null-device" value="true" />
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="run_fuzz_service_test.sh"/>
+        <option name="relative-path-execution" value="true" />
+    </test>
+</configuration>
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
new file mode 100644
index 0000000..cec52fd
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# 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.
+
+color_success=$'\E'"[0;32m"
+color_failed=$'\E'"[0;31m"
+color_reset=$'\E'"[00m"
+
+FUZZER_NAME=test_service_fuzzer_should_crash
+FUZZER_OUT=fuzzer-output
+
+if [ ! -f "$FUZZER_NAME" ]
+then
+    echo -e "${color_failed}Binary $FUZZER_NAME does not exist"
+    echo "${color_reset}"
+    exit 1
+fi
+
+echo "INFO: Running fuzzer : test_service_fuzzer_should_crash"
+
+./test_service_fuzzer_should_crash -max_total_time=30 &>${FUZZER_OUT}
+
+echo "INFO: Searching fuzzer output for expected crashes"
+if grep -q "Expected crash in set" ${FUZZER_OUT};
+then
+    echo -e "${color_success}Success: Found expected crash. fuzzService test successful!"
+else
+    echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash"
+    echo "${color_reset}"
+    exit 1
+fi
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 910c9dc..a6fd487 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -51,8 +51,10 @@
     sp<RpcSession> session = RpcSession::make();
     session->setMaxIncomingThreads(1);
     status_t status;
-    for (size_t tries = 0; tries < 5; tries++) {
-        usleep(10000);
+
+    // b/274084938 - ASAN may be slow, wait a while
+    for (size_t tries = 0; tries < 50; tries++) {
+        usleep(100000);
         status = session->setupUnixDomainClient(addr.c_str());
         if (status == OK) break;
     }
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 68b0008..8f64323 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -67,7 +67,7 @@
 
     // 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);
+    LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0));
 
     if (mPortAcl) {
         // Initialize the array of pointers to uuids.
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index d249b2e..692f82d 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -29,8 +29,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport for Trusty.
 class RpcTransportTipcTrusty : public RpcTransport {
 public:
@@ -282,8 +280,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const {
     return std::make_unique<RpcTransportCtxTipcTrusty>();
 }
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
index d8b080f..1cefac5 100644
--- a/libs/binder/trusty/binderRpcTest/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -1,6 +1,6 @@
 {
     "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
     "app_name": "binderRpcTest",
-    "min_heap": 163840,
+    "min_heap": 262144,
     "min_stack": 16384
 }
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 6678eb8..8924b36 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -59,14 +59,17 @@
             size_t msgMaxSize,
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
-    void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    [[nodiscard]] bool setProtocolVersion(uint32_t version) {
+        return mRpcServer->setProtocolVersion(version);
+    }
     void setSupportedFileDescriptorTransportModes(
             const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
         mRpcServer->setSupportedFileDescriptorTransportModes(modes);
     }
     void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
     void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
-    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
+    void setPerSessionRootObject(
+            std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object) {
         mRpcServer->setPerSessionRootObject(std::move(object));
     }
     sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); }
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bf34987..bf2d7b6 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -247,6 +247,7 @@
 
     shared_libs: [
         "libbinder",
+        "libGLESv2",
     ],
 
     export_shared_lib_headers: [
@@ -372,7 +373,6 @@
         "libbase",
         "libcutils",
         "libEGL",
-        "libGLESv2",
         "libhidlbase",
         "liblog",
         "libnativewindow",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9a2343b..808388f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -630,7 +630,8 @@
     BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
             *outSlot,
             mSlots[*outSlot].mFrameNumber,
-            mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+            mSlots[*outSlot].mGraphicBuffer != nullptr ?
+            mSlots[*outSlot].mGraphicBuffer->handle : nullptr, returnFlags);
 
     if (outBufferAge) {
         *outBufferAge = mCore->mBufferAge;
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 82e1b5a..872b069 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -72,6 +72,14 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 155276,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libgui library",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 1dddeba..bf354e7 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -46,6 +46,8 @@
     // where the previous frame was presented in the current frame's expected vsync. This pushes the
     // current frame to the next vsync. The behavior is similar to BufferStuffing.
     SurfaceFlingerStuffing = 0x100,
+    // Frame was dropped, as a newer frame was ready and replaced this frame.
+    Dropped = 0x200,
 };
 
 } // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 869458c..4be7328 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -85,6 +85,12 @@
         "-Wl,--exclude-libs=libtflite_static.a",
     ],
 
+    sanitize: {
+        undefined: true,
+        all_undefined: true,
+        misc_undefined: ["integer"],
+    },
+
     static_libs: [
         "libui-types",
         "libtflite_static",
@@ -117,10 +123,6 @@
                 "libgui_window_info_static",
             ],
 
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-
             required: [
                 "motion_predictor_model_prebuilt",
             ],
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 3037573..a425b93 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -25,9 +25,9 @@
 #include <string>
 #include <vector>
 
+#include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <android/input.h>
-#include <log/log.h>
 
 #include <attestation/HmacKeyManager.h>
 #include <ftl/enum.h>
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 5720099..c835a08 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -37,7 +37,7 @@
     reset();
 }
 
-VelocityControlParameters& VelocityControl::getParameters() {
+const VelocityControlParameters& VelocityControl::getParameters() const{
     return mParameters;
 }
 
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8551e5f..87c7768 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -22,7 +22,6 @@
 #include <math.h>
 #include <optional>
 
-#include <android-base/stringprintf.h>
 #include <input/PrintTools.h>
 #include <input/VelocityTracker.h>
 #include <utils/BitSet.h>
@@ -56,6 +55,9 @@
 // Nanoseconds per milliseconds.
 static const nsecs_t NANOS_PER_MS = 1000000;
 
+// Seconds per nanosecond.
+static const float SECONDS_PER_NANO = 1E-9;
+
 // All axes supported for velocity tracking, mapped to their default strategies.
 // Although other strategies are available for testing and comparison purposes,
 // the default strategy is the one that applications will actually use.  Be very careful
@@ -268,12 +270,8 @@
               ", activePointerId=%s",
               eventTime, pointerId, toString(mActivePointerId).c_str());
 
-        std::optional<Estimator> estimator = getEstimator(axis, pointerId);
-        ALOGD("  %d: axis=%d, position=%0.3f, "
-              "estimator (degree=%d, coeff=%s, confidence=%f)",
-              pointerId, axis, position, int((*estimator).degree),
-              vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(),
-              (*estimator).confidence);
+        ALOGD("  %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
+              toString(getVelocity(axis, pointerId)).c_str());
     }
 }
 
@@ -349,9 +347,9 @@
 }
 
 std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const {
-    std::optional<Estimator> estimator = getEstimator(axis, pointerId);
-    if (estimator && (*estimator).degree >= 1) {
-        return (*estimator).coeff[1];
+    const auto& it = mConfiguredStrategies.find(axis);
+    if (it != mConfiguredStrategies.end()) {
+        return it->second->getVelocity(pointerId);
     }
     return {};
 }
@@ -374,56 +372,52 @@
     return computedVelocity;
 }
 
-std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis,
-                                                                        int32_t pointerId) const {
-    const auto& it = mConfiguredStrategies.find(axis);
-    if (it == mConfiguredStrategies.end()) {
-        return std::nullopt;
+AccumulatingVelocityTrackerStrategy::AccumulatingVelocityTrackerStrategy(
+        nsecs_t horizonNanos, bool maintainHorizonDuringAdd)
+      : mHorizonNanos(horizonNanos), mMaintainHorizonDuringAdd(maintainHorizonDuringAdd) {}
+
+void AccumulatingVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mMovements.erase(pointerId);
+}
+
+void AccumulatingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                      float position) {
+    auto [ringBufferIt, _] = mMovements.try_emplace(pointerId, HISTORY_SIZE);
+    RingBuffer<Movement>& movements = ringBufferIt->second;
+    const size_t size = movements.size();
+
+    if (size != 0 && movements[size - 1].eventTime == eventTime) {
+        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
+        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
+        // the new pointer. If the eventtimes for both events are identical, just update the data
+        // for this time (i.e. pop out the last element, and insert the updated movement).
+        // We only compare against the last value, as it is likely that addMovement is called
+        // in chronological order as events occur.
+        movements.popBack();
     }
-    return it->second->getEstimator(pointerId);
+
+    movements.pushBack({eventTime, position});
+
+    // Clear movements that do not fall within `mHorizonNanos` of the latest movement.
+    // Note that, if in the future we decide to use more movements (i.e. increase HISTORY_SIZE),
+    // we can consider making this step binary-search based, which will give us some improvement.
+    if (mMaintainHorizonDuringAdd) {
+        while (eventTime - movements[0].eventTime > mHorizonNanos) {
+            movements.popFront();
+        }
+    }
 }
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree,
                                                                          Weighting weighting)
-      : mDegree(degree), mWeighting(weighting) {}
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            true /*maintainHorizonDuringAdd*/),
+        mDegree(degree),
+        mWeighting(weighting) {}
 
-LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
-}
-
-void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                      float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
+LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
 
 /**
  * Solves a linear least squares problem to obtain a N degree polynomial that fits
@@ -474,10 +468,9 @@
  * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
  * http://en.wikipedia.org/wiki/Gram-Schmidt
  */
-static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
-                              const std::vector<float>& w, uint32_t n,
-                              std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB,
-                              float* outDet) {
+static std::optional<float> solveLeastSquares(const std::vector<float>& x,
+                                              const std::vector<float>& y,
+                                              const std::vector<float>& w, uint32_t n) {
     const size_t m = x.size();
 
     ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
@@ -515,7 +508,7 @@
         if (norm < 0.000001f) {
             // vectors are linearly dependent or zero so no solution
             ALOGD_IF(DEBUG_STRATEGY, "  - no solution, norm=%f", norm);
-            return false;
+            return {};
         }
 
         float invNorm = 1.0f / norm;
@@ -549,6 +542,7 @@
     for (uint32_t h = 0; h < m; h++) {
         wy[h] = y[h] * w[h];
     }
+    std::array<float, VelocityTracker::MAX_DEGREE + 1> outB;
     for (uint32_t i = n; i != 0; ) {
         i--;
         outB[i] = vectorDot(&q[i][0], wy, m);
@@ -570,42 +564,46 @@
     }
     ymean /= m;
 
-    float sserr = 0;
-    float sstot = 0;
-    for (uint32_t h = 0; h < m; h++) {
-        float err = y[h] - outB[0];
-        float term = 1;
-        for (uint32_t i = 1; i < n; i++) {
-            term *= x[h];
-            err -= term * outB[i];
+    if (DEBUG_STRATEGY) {
+        float sserr = 0;
+        float sstot = 0;
+        for (uint32_t h = 0; h < m; h++) {
+            float err = y[h] - outB[0];
+            float term = 1;
+            for (uint32_t i = 1; i < n; i++) {
+                term *= x[h];
+                err -= term * outB[i];
+            }
+            sserr += w[h] * w[h] * err * err;
+            float var = y[h] - ymean;
+            sstot += w[h] * w[h] * var * var;
         }
-        sserr += w[h] * w[h] * err * err;
-        float var = y[h] - ymean;
-        sstot += w[h] * w[h] * var * var;
+        ALOGD("  - sserr=%f", sserr);
+        ALOGD("  - sstot=%f", sstot);
     }
-    *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "  - sserr=%f", sserr);
-    ALOGD_IF(DEBUG_STRATEGY, "  - sstot=%f", sstot);
-    ALOGD_IF(DEBUG_STRATEGY, "  - det=%f", *outDet);
-
-    return true;
+    return outB[1];
 }
 
 /*
  * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
  * the default implementation
  */
-static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
-        const std::vector<float>& x, const std::vector<float>& y) {
-    const size_t count = x.size();
-    LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
-    // Solving y = a*x^2 + b*x + c
+std::optional<float> LeastSquaresVelocityTrackerStrategy::solveUnweightedLeastSquaresDeg2(
+        const RingBuffer<Movement>& movements) const {
+    // Solving y = a*x^2 + b*x + c, where
+    //      - "x" is age (i.e. duration since latest movement) of the movemnets
+    //      - "y" is positions of the movements.
     float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
 
+    const size_t count = movements.size();
+    const Movement& newestMovement = movements[count - 1];
     for (size_t i = 0; i < count; i++) {
-        float xi = x[i];
-        float yi = y[i];
+        const Movement& movement = movements[i];
+        nsecs_t age = newestMovement.eventTime - movement.eventTime;
+        float xi = -age * SECONDS_PER_NANO;
+        float yi = movement.position;
+
         float xi2 = xi*xi;
         float xi3 = xi2*xi;
         float xi4 = xi3*xi;
@@ -632,124 +630,68 @@
         ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
         return std::nullopt;
     }
-    // Compute a
-    float numerator = Sx2y*Sxx - Sxy*Sxx2;
-    float a = numerator / denominator;
 
-    // Compute b
-    numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
-    float b = numerator / denominator;
-
-    // Compute c
-    float c = syi/count - b * sxi/count - a * sxi2/count;
-
-    return std::make_optional(std::array<float, 3>({c, b, a}));
+    return (Sxy * Sx2x2 - Sx2y * Sxx2) / denominator;
 }
 
-std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> LeastSquaresVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
+
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
+        return std::nullopt; // no data
+    }
+
+    uint32_t degree = mDegree;
+    if (degree > size - 1) {
+        degree = size - 1;
+    }
+
+    if (degree <= 0) {
+        return std::nullopt;
+    }
+
+    if (degree == 2 && mWeighting == Weighting::NONE) {
+        // Optimize unweighted, quadratic polynomial fit
+        return solveUnweightedLeastSquaresDeg2(movements);
+    }
+
     // Iterate over movement samples in reverse time order and collect samples.
     std::vector<float> positions;
     std::vector<float> w;
     std::vector<float> time;
 
-    uint32_t index = mIndex.at(pointerId);
-    const Movement& newestMovement = movementIt->second[index];
-    do {
-        const Movement& movement = movementIt->second[index];
-
+    const Movement& newestMovement = movements[size - 1];
+    for (ssize_t i = size - 1; i >= 0; i--) {
+        const Movement& movement = movements[i];
         nsecs_t age = newestMovement.eventTime - movement.eventTime;
-        if (age > HORIZON) {
-            break;
-        }
-        if (movement.eventTime == 0 && index != 0) {
-            // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's
-            // possible that not all entries are valid. We use a time=0 as a signal for those
-            // uninitialized values. If we encounter a time of 0 in a position
-            // that's > 0, it means that we hit the block where the data wasn't initialized.
-            // We still don't know whether the value at index=0, with eventTime=0 is valid.
-            // However, that's only possible when the value is by itself. So there's no hard in
-            // processing it anyways, since the velocity for a single point is zero, and this
-            // situation will only be encountered in artificial circumstances (in tests).
-            // In practice, time will never be 0.
-            break;
-        }
         positions.push_back(movement.position);
-        w.push_back(chooseWeight(pointerId, index));
+        w.push_back(chooseWeight(pointerId, i));
         time.push_back(-age * 0.000000001f);
-        index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (positions.size() < HISTORY_SIZE);
-
-    const size_t m = positions.size();
-    if (m == 0) {
-        return std::nullopt; // no data
     }
 
-    // Calculate a least squares polynomial fit.
-    uint32_t degree = mDegree;
-    if (degree > m - 1) {
-        degree = m - 1;
-    }
-
-    if (degree == 2 && mWeighting == Weighting::NONE) {
-        // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> coeff =
-                solveUnweightedLeastSquaresDeg2(time, positions);
-        if (coeff) {
-            VelocityTracker::Estimator estimator;
-            estimator.time = newestMovement.eventTime;
-            estimator.degree = 2;
-            estimator.confidence = 1;
-            for (size_t i = 0; i <= estimator.degree; i++) {
-                estimator.coeff[i] = (*coeff)[i];
-            }
-            return estimator;
-        }
-    } else if (degree >= 1) {
-        // General case for an Nth degree polynomial fit
-        float det;
-        uint32_t n = degree + 1;
-        VelocityTracker::Estimator estimator;
-        if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) {
-            estimator.time = newestMovement.eventTime;
-            estimator.degree = degree;
-            estimator.confidence = det;
-
-            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
-                     int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(),
-                     estimator.confidence);
-
-            return estimator;
-        }
-    }
-
-    // No velocity data available for this pointer, but we do have its current position.
-    VelocityTracker::Estimator estimator;
-    estimator.coeff[0] = positions[0];
-    estimator.time = newestMovement.eventTime;
-    estimator.degree = 0;
-    estimator.confidence = 1;
-    return estimator;
+    // General case for an Nth degree polynomial fit
+    return solveLeastSquares(time, positions, w, degree + 1);
 }
 
 float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const {
-    const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId);
+    const RingBuffer<Movement>& movements = mMovements.at(pointerId);
+    const size_t size = movements.size();
     switch (mWeighting) {
         case Weighting::DELTA: {
             // Weight points based on how much time elapsed between them and the next
             // point so that points that "cover" a shorter time span are weighed less.
             //   delta  0ms: 0.5
             //   delta 10ms: 1.0
-            if (index == mIndex.at(pointerId)) {
+            if (index == size - 1) {
                 return 1.0f;
             }
-            uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
             float deltaMillis =
-                    (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f;
+                    (movements[index + 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (deltaMillis < 0) {
                 return 0.5f;
             }
@@ -766,8 +708,7 @@
             //   age 50ms: 1.0
             //   age 60ms: 0.5
             float ageMillis =
-                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
-                    0.000001f;
+                    (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (ageMillis < 0) {
                 return 0.5f;
             }
@@ -789,8 +730,7 @@
             //   age  50ms: 1.0
             //   age 100ms: 0.5
             float ageMillis =
-                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
-                    0.000001f;
+                    (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (ageMillis < 50) {
                 return 1.0f;
             }
@@ -830,13 +770,9 @@
     mPointerIdBits.markBit(pointerId);
 }
 
-std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> IntegratingVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     if (mPointerIdBits.hasBit(pointerId)) {
-        const State& state = mPointerState[pointerId];
-        VelocityTracker::Estimator estimator;
-        populateEstimator(state, &estimator);
-        return estimator;
+        return mPointerState[pointerId].vel;
     }
 
     return std::nullopt;
@@ -886,77 +822,39 @@
     state.pos = pos;
 }
 
-void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->time = state.updateTime;
-    outEstimator->confidence = 1.0f;
-    outEstimator->degree = state.degree;
-    outEstimator->coeff[0] = state.pos;
-    outEstimator->coeff[1] = state.vel;
-    outEstimator->coeff[2] = state.accel / 2;
-}
-
-
 // --- LegacyVelocityTrackerStrategy ---
 
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {}
+LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy()
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            false /*maintainHorizonDuringAdd*/) {}
 
 LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
 }
 
-void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
-
-std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> LegacyVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
-    const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)];
+
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
+        return std::nullopt; // no data
+    }
+
+    const Movement& newestMovement = movements[size - 1];
 
     // Find the oldest sample that contains the pointer and that is not older than HORIZON.
     nsecs_t minTime = newestMovement.eventTime - HORIZON;
-    uint32_t oldestIndex = mIndex.at(pointerId);
-    uint32_t numTouches = 1;
-    do {
-        uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
-        const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex];
+    uint32_t oldestIndex = size - 1;
+    for (ssize_t i = size - 1; i >= 0; i--) {
+        const Movement& nextOldestMovement = movements[i];
         if (nextOldestMovement.eventTime < minTime) {
             break;
         }
-        oldestIndex = nextOldestIndex;
-    } while (++numTouches < HISTORY_SIZE);
+        oldestIndex = i;
+    }
 
     // Calculate an exponentially weighted moving average of the velocity estimate
     // at different points in time measured relative to the oldest sample.
@@ -970,17 +868,13 @@
     // 16ms apart but some consecutive samples could be only 0.5sm apart because
     // the hardware or driver reports them irregularly or in bursts.
     float accumV = 0;
-    uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
-    const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex];
+    const Movement& oldestMovement = movements[oldestIndex];
     float oldestPosition = oldestMovement.position;
     nsecs_t lastDuration = 0;
 
-    while (numTouches-- > 1) {
-        if (++index == HISTORY_SIZE) {
-            index = 0;
-        }
-        const Movement& movement = mMovements.at(pointerId)[index];
+    for (size_t i = oldestIndex; i < size; i++) {
+        const Movement& movement = movements[i];
         nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
 
         // If the duration between samples is small, we may significantly overestimate
@@ -996,62 +890,22 @@
         }
     }
 
-    // Report velocity.
-    float newestPosition = newestMovement.position;
-    VelocityTracker::Estimator estimator;
-    estimator.time = newestMovement.eventTime;
-    estimator.confidence = 1;
-    estimator.coeff[0] = newestPosition;
     if (samplesUsed) {
-        estimator.coeff[1] = accumV;
-        estimator.degree = 1;
-    } else {
-        estimator.degree = 0;
+        return accumV;
     }
-    return estimator;
+    return std::nullopt;
 }
 
 // --- ImpulseVelocityTrackerStrategy ---
 
 ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
-      : mDeltaValues(deltaValues) {}
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            true /*maintainHorizonDuringAdd*/),
+        mDeltaValues(deltaValues) {}
 
 ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
 }
 
-void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                 float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
-
 /**
  * Calculate the total impulse provided to the screen and the resulting velocity.
  *
@@ -1126,112 +980,44 @@
     return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
 }
 
-static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count,
-                                      bool deltaValues) {
-    // The input should be in reversed time order (most recent sample at index i=0)
-    // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
-    static constexpr float SECONDS_PER_NANO = 1E-9;
-
-    if (count < 2) {
-        return 0; // if 0 or 1 points, velocity is zero
-    }
-    if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
-        ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
-    }
-
-    // If the data values are delta values, we do not have to calculate deltas here.
-    // We can use the delta values directly, along with the calculated time deltas.
-    // Since the data value input is in reversed time order:
-    //      [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1])
-    //      [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1])
-    // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4].
-    // Since the input is in reversed time order, the input values for this function would be
-    // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively.
-    //
-    // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1}
-    // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4}
-
-    if (count == 2) { // if 2 points, basic linear calculation
-        if (t[1] == t[0]) {
-            ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
-            return 0;
-        }
-        const float deltaX = deltaValues ? -x[0] : x[1] - x[0];
-        return deltaX / (SECONDS_PER_NANO * (t[1] - t[0]));
-    }
-    // Guaranteed to have at least 3 points here
-    float work = 0;
-    for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time
-        if (t[i] == t[i-1]) {
-            ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]);
-            continue;
-        }
-        float vprev = kineticEnergyToVelocity(work); // v[i-1]
-        const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1];
-        float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
-        work += (vcurr - vprev) * fabsf(vcurr);
-        if (i == count - 1) {
-            work *= 0.5; // initial condition, case 2) above
-        }
-    }
-    return kineticEnergyToVelocity(work);
-}
-
-std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> ImpulseVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
 
-    // Iterate over movement samples in reverse time order and collect samples.
-    float positions[HISTORY_SIZE];
-    nsecs_t time[HISTORY_SIZE];
-    size_t m = 0; // number of points that will be used for fitting
-    size_t index = mIndex.at(pointerId);
-    const Movement& newestMovement = movementIt->second[index];
-    do {
-        const Movement& movement = movementIt->second[index];
-
-        nsecs_t age = newestMovement.eventTime - movement.eventTime;
-        if (age > HORIZON) {
-            break;
-        }
-        if (movement.eventTime == 0 && index != 0) {
-            // All eventTime's are initialized to 0. If we encounter a time of 0 in a position
-            // that's >0, it means that we hit the block where the data wasn't initialized.
-            // It's also possible that the sample at 0 would be invalid, but there's no harm in
-            // processing it, since it would be just a single point, and will only be encountered
-            // in artificial circumstances (in tests).
-            break;
-        }
-
-        positions[m] = movement.position;
-        time[m] = movement.eventTime;
-        index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (++m < HISTORY_SIZE);
-
-    if (m == 0) {
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
         return std::nullopt; // no data
     }
-    VelocityTracker::Estimator estimator;
-    estimator.coeff[0] = 0;
-    estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
-    estimator.coeff[2] = 0;
 
-    estimator.time = newestMovement.eventTime;
-    estimator.degree = 2; // similar results to 2nd degree fit
-    estimator.confidence = 1;
+    float work = 0;
+    for (size_t i = 0; i < size - 1; i++) {
+        const Movement& mvt = movements[i];
+        const Movement& nextMvt = movements[i + 1];
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]);
+        float vprev = kineticEnergyToVelocity(work);
+        float delta = mDeltaValues ? nextMvt.position : nextMvt.position - mvt.position;
+        float vcurr = delta / (SECONDS_PER_NANO * (nextMvt.eventTime - mvt.eventTime));
+        work += (vcurr - vprev) * fabsf(vcurr);
+
+        if (i == 0) {
+            work *= 0.5; // initial condition, case 2) above
+        }
+    }
+
+    const float velocity = kineticEnergyToVelocity(work);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", velocity);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
         // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
         // X axis chosen arbitrarily for velocity comparisons.
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
-        for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]);
+        for (size_t i = 0; i < size; i++) {
+            const Movement& mvt = movements[i];
+            lsq2.addMovement(mvt.eventTime, pointerId, AMOTION_EVENT_AXIS_X, mvt.position);
         }
         std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
         if (v) {
@@ -1240,7 +1026,7 @@
             ALOGD("lsq2 velocity: could not compute velocity");
         }
     }
-    return estimator;
+    return velocity;
 }
 
 } // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 42bdf57..6aae25d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -44,12 +44,20 @@
         "-Wno-unused-parameter",
     ],
     sanitize: {
+        hwaddress: true,
         undefined: true,
         all_undefined: true,
         diag: {
             undefined: true,
         },
     },
+    target: {
+        host: {
+            sanitize: {
+                address: true,
+            },
+        },
+    },
     shared_libs: [
         "libbase",
         "libbinder",
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ae72109..ffebff1 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -42,8 +42,8 @@
 // here EV = expected value, tol = VELOCITY_TOLERANCE
 constexpr float VELOCITY_TOLERANCE = 0.2;
 
-// estimate coefficients must be within 0.001% of the target value
-constexpr float COEFFICIENT_TOLERANCE = 0.00001;
+// quadratic velocity must be within 0.001% of the target value
+constexpr float QUADRATIC_VELOCITY_TOLERANCE = 0.00001;
 
 // --- VelocityTrackerTest ---
 class VelocityTrackerTest : public testing::Test { };
@@ -76,10 +76,6 @@
     }
 }
 
-static void checkCoefficient(float actual, float target) {
-    EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
-}
-
 struct Position {
     float x;
     float y;
@@ -284,21 +280,20 @@
     checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
 }
 
-static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
-                                             const std::array<float, 3>& coefficients) {
+static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
+                                             float velocity) {
     VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
     std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0);
-    std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0);
-    EXPECT_TRUE(estimatorX);
-    EXPECT_TRUE(estimatorY);
-    for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient((*estimatorX).coeff[i], coefficients[i]);
-        checkCoefficient((*estimatorY).coeff[i], coefficients[i]);
-    }
+    std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0);
+    std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+    ASSERT_TRUE(velocityX);
+    ASSERT_TRUE(velocityY);
+
+    EXPECT_NEAR_BY_FRACTION(*velocityX, velocity, QUADRATIC_VELOCITY_TOLERANCE);
+    EXPECT_NEAR_BY_FRACTION(*velocityY, velocity, QUADRATIC_VELOCITY_TOLERANCE);
 }
 
 /*
@@ -461,8 +456,6 @@
 
     EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
 
-    EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
-
     VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000);
     for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
         EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id));
@@ -1074,7 +1067,7 @@
  * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be
  * part of the fitted data), this can cause large velocity values to be reported instead.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_ThreeFingerTap) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0us,      {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
         { 10800us,  {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN
@@ -1162,7 +1155,7 @@
  * ================== Tests for least squares fitting ==============================================
  *
  * Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy
- * getEstimator function. In particular:
+ * getVelocity function. In particular:
  * - inside the function, time gets converted from nanoseconds to seconds
  *   before being used in the fit.
  * - any values that are older than 100 ms are being discarded.
@@ -1183,7 +1176,7 @@
  * The coefficients are (0, 0, 1).
  * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Constant) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} }, // 0 s
         { 1ms, {{1, 1}} }, // 0.001 s
@@ -1195,13 +1188,13 @@
     // -0.002, 1
     // -0.001, 1
     // -0.ms, 1
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({1, 0, 0}));
+    computeAndCheckQuadraticVelocity(motions, 0);
 }
 
 /*
  * Straight line y = x :: the constant and quadratic coefficients are zero.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Linear) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{-2, -2}} },
         { 1ms, {{-1, -1}} },
@@ -1213,13 +1206,13 @@
     // -0.002, -2
     // -0.001, -1
     // -0.000,  0
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 1E3, 0}));
+    computeAndCheckQuadraticVelocity(motions, 1E3);
 }
 
 /*
  * Parabola
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
@@ -1231,13 +1224,13 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 8
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+    computeAndCheckQuadraticVelocity(motions, 4.5E3);
 }
 
 /*
  * Parabola
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic2) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
@@ -1249,13 +1242,13 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 9
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({9, 6E3, 1E6}));
+    computeAndCheckQuadraticVelocity(motions, 6E3);
 }
 
 /*
  * Parabola :: y = x^2 :: the constant and linear coefficients are zero.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic3) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{4, 4}} },
         { 1ms, {{1, 1}} },
@@ -1267,7 +1260,7 @@
     // -0.002, 4
     // -0.001, 1
     // -0.000, 0
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6}));
+    computeAndCheckQuadraticVelocity(motions, 0E3);
 }
 
 // Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity.
diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h
index 1659d54..e269f0d 100644
--- a/libs/nativewindow/include/android/hardware_buffer_aidl.h
+++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h
@@ -34,6 +34,10 @@
 #include <android/hardware_buffer.h>
 #include <sys/cdefs.h>
 
+#ifdef __cplusplus
+#include <string>
+#endif
+
 __BEGIN_DECLS
 
 /**
@@ -142,6 +146,15 @@
         return ret;
     }
 
+    inline std::string toString() const {
+        if (!mBuffer) {
+            return "<HardwareBuffer: Invalid>";
+        }
+        uint64_t id = 0;
+        AHardwareBuffer_getId(mBuffer, &id);
+        return "<HardwareBuffer " + std::to_string(id) + ">";
+    }
+
 private:
     HardwareBuffer(const HardwareBuffer& other) = delete;
     HardwareBuffer& operator=(const HardwareBuffer& other) = delete;
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index c412c9c..fc9b4da 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,6 +20,11 @@
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <SkImage.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+
+#include <android/hardware_buffer.h>
 #include "ColorSpaces.h"
 #include "log/log_main.h"
 #include "utils/Trace.h"
@@ -79,7 +84,7 @@
 
 // releaseImageProc is invoked by SkImage, when the texture is no longer in use.
 // "releaseContext" contains an "AutoBackendTexture*".
-void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) {
+void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContext) {
     AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
     textureRelease->unref(false);
 }
@@ -112,8 +117,9 @@
     }
 
     sk_sp<SkImage> image =
-            SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType,
-                                     alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
+            SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin,
+                                        colorType, alphaType, toSkColorSpace(dataspace),
+                                        releaseImageProc, this);
     if (image.get()) {
         // The following ref will be counteracted by releaseProc, when SkImage is discarded.
         ref();
@@ -133,10 +139,10 @@
     LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
     if (!mSurface.get() || mDataspace != dataspace) {
         sk_sp<SkSurface> surface =
-                SkSurface::MakeFromBackendTexture(context, mBackendTexture,
-                                                  kTopLeft_GrSurfaceOrigin, 0, mColorType,
-                                                  toSkColorSpace(dataspace), nullptr,
-                                                  releaseSurfaceProc, this);
+                SkSurfaces::WrapBackendTexture(context, mBackendTexture,
+                                               kTopLeft_GrSurfaceOrigin, 0, mColorType,
+                                               toSkColorSpace(dataspace), nullptr,
+                                               releaseSurfaceProc, this);
         if (surface.get()) {
             // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
             ref();
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 00b901b..509ac40 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -144,7 +144,7 @@
     CleanupManager& mCleanupMgr;
 
     static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
-    static void releaseImageProc(SkImage::ReleaseContext releaseContext);
+    static void releaseImageProc(SkImages::ReleaseContext releaseContext);
 
     int mUsageCount = 0;
 
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index 511d7c9..a77d5bf 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -25,6 +25,7 @@
 #include <SkSize.h>
 #include <SkString.h>
 #include <SkSurface.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include "include/gpu/GpuTypes.h" // from Skia
 #include <log/log.h>
 #include <utils/Trace.h>
@@ -45,8 +46,8 @@
     // Create blur surface with the bit depth and colorspace of the original surface
     SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
                                                        std::ceil(blurRect.height() * kInputScale));
-    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context,
-                                                           skgpu::Budgeted::kNo, scaledInfo);
+    sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+                                                        skgpu::Budgeted::kNo, scaledInfo);
 
     SkPaint paint;
     paint.setBlendMode(SkBlendMode::kSrc);
diff --git a/services/surfaceflinger/Display/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h
similarity index 94%
rename from services/surfaceflinger/Display/DisplayMap.h
rename to libs/ui/include/ui/DisplayMap.h
index 0d59706..7eacb0a 100644
--- a/services/surfaceflinger/Display/DisplayMap.h
+++ b/libs/ui/include/ui/DisplayMap.h
@@ -19,7 +19,7 @@
 #include <ftl/small_map.h>
 #include <ftl/small_vector.h>
 
-namespace android::display {
+namespace android::ui {
 
 // The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
 
@@ -32,4 +32,4 @@
 template <typename T>
 using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
 
-} // namespace android::display
+} // namespace android::ui
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index f1122fd..9a6bb7a 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -152,6 +152,7 @@
 
     if (!angleGetDisplayPlatform) {
         ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
+        dlclose(so);
         return false;
     }
 
@@ -162,6 +163,7 @@
     if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
                                     &platformMethods))) {
         ALOGE("ANGLEGetDisplayPlatform call failed!");
+        dlclose(so);
         return false;
     }
     if (platformMethods) {
diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp
index 8681784..c88f2fc 100644
--- a/services/displayservice/Android.bp
+++ b/services/displayservice/Android.bp
@@ -23,7 +23,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library_static {
     name: "libdisplayservicehidl",
 
     srcs: [
@@ -37,18 +37,24 @@
         "libgui",
         "libhidlbase",
         "libutils",
+    ],
+
+    static_libs: [
         "android.frameworks.displayservice@1.0",
     ],
 
     export_include_dirs: ["include"],
     export_shared_lib_headers: [
-        "android.frameworks.displayservice@1.0",
         "libgui",
         "libutils",
     ],
 
+    export_static_lib_headers: [
+        "android.frameworks.displayservice@1.0",
+    ],
+
     cflags: [
         "-Werror",
         "-Wall",
-    ]
+    ],
 }
diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..6bcc5e8
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+    name: "gpu_service_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    static_libs: [
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "paulthomson@google.com",
+            "pbaiget@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+    include_dirs: ["frameworks/native/services/gpuservice/"],
+    srcs: ["GpuServiceFuzzer.cpp"],
+    shared_libs: [
+        "libgpuservice",
+    ],
+}
diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
new file mode 100644
index 0000000..c2574a3
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GpuService.h"
+
+using ::android::fuzzService;
+using ::android::GpuService;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    sp<GpuService> gpuService = new GpuService();
+    fuzzService(gpuService, FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 69df45b..ffcc967 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -60,6 +60,7 @@
     name: "libinputflinger_sources",
     srcs: [
         "InputCommonConverter.cpp",
+        "InputDeviceMetricsCollector.cpp",
         "InputProcessor.cpp",
         "PreferStylusOverTouchBlocker.cpp",
         "UnwantedInteractionBlocker.cpp",
@@ -129,6 +130,7 @@
         "libinputflinger_base",
         "libinputreader",
         "libinputreporter",
+        "libPlatformProperties",
     ],
     static_libs: [
         "libinputdispatcher",
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
new file mode 100644
index 0000000..c6089c8
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputDeviceMetricsCollector"
+#include "InputDeviceMetricsCollector.h"
+
+namespace android {
+
+InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
+      : mNextListener(listener){};
+
+void InputDeviceMetricsCollector::notifyInputDevicesChanged(
+        const NotifyInputDevicesChangedArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyConfigurationChanged(
+        const NotifyConfigurationChangedArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
+        const NotifyPointerCaptureChangedArgs& args) {
+    mNextListener.notify(args);
+}
+
+void InputDeviceMetricsCollector::dump(std::string& dump) {
+    dump += "InputDeviceMetricsCollector:\n";
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
new file mode 100644
index 0000000..c959075
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "InputListener.h"
+
+namespace android {
+
+/**
+ * Logs metrics about registered input devices and their usages.
+ *
+ * Not thread safe. Must be called from a single thread.
+ */
+class InputDeviceMetricsCollectorInterface : public InputListenerInterface {
+public:
+    /**
+     * Dump the state of the interaction blocker.
+     * This method may be called on any thread (usually by the input manager on a binder thread).
+     */
+    virtual void dump(std::string& dump) = 0;
+};
+
+class InputDeviceMetricsCollector : public InputDeviceMetricsCollectorInterface {
+public:
+    explicit InputDeviceMetricsCollector(InputListenerInterface& listener);
+    ~InputDeviceMetricsCollector() override = default;
+
+    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 dump(std::string& dump) override;
+
+private:
+    InputListenerInterface& mNextListener;
+};
+
+} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index ddebcad..863b483 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -23,6 +23,7 @@
 #include "InputReaderFactory.h"
 #include "UnwantedInteractionBlocker.h"
 
+#include <android/sysprop/InputProperties.sysprop.h>
 #include <binder/IPCThreadState.h>
 
 #include <log/log.h>
@@ -32,6 +33,9 @@
 
 namespace android {
 
+static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
+        sysprop::InputProperties::enable_input_device_usage_metrics().value_or(false);
+
 using gui::FocusRequest;
 
 static int32_t exceptionCodeFromStatusT(status_t status) {
@@ -55,12 +59,22 @@
 
 /**
  * The event flow is via the "InputListener" interface, as follows:
- * InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher
+ *   InputReader
+ *     -> UnwantedInteractionBlocker
+ *     -> InputProcessor
+ *     -> InputDeviceMetricsCollector
+ *     -> InputDispatcher
  */
 InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
                            InputDispatcherPolicyInterface& dispatcherPolicy) {
     mDispatcher = createInputDispatcher(dispatcherPolicy);
-    mProcessor = std::make_unique<InputProcessor>(*mDispatcher);
+
+    if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
+        mCollector = std::make_unique<InputDeviceMetricsCollector>(*mDispatcher);
+    }
+
+    mProcessor = ENABLE_INPUT_DEVICE_USAGE_METRICS ? std::make_unique<InputProcessor>(*mCollector)
+                                                   : std::make_unique<InputProcessor>(*mDispatcher);
     mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor);
     mReader = createInputReader(readerPolicy, *mBlocker);
 }
@@ -131,6 +145,10 @@
     dump += '\n';
     mProcessor->dump(dump);
     dump += '\n';
+    if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
+        mCollector->dump(dump);
+        dump += '\n';
+    }
     mDispatcher->dump(dump);
     dump += '\n';
 }
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index b6ad419..0f0d8ea 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -20,6 +20,7 @@
  * Native input manager.
  */
 
+#include "InputDeviceMetricsCollector.h"
 #include "InputProcessor.h"
 #include "InputReaderBase.h"
 #include "include/UnwantedInteractionBlockerInterface.h"
@@ -82,7 +83,7 @@
     /* Gets the input reader. */
     virtual InputReaderInterface& getReader() = 0;
 
-    /* Gets the input processor */
+    /* Gets the input processor. */
     virtual InputProcessorInterface& getProcessor() = 0;
 
     /* Gets the input dispatcher. */
@@ -124,6 +125,8 @@
 
     std::unique_ptr<InputProcessorInterface> mProcessor;
 
+    std::unique_ptr<InputDeviceMetricsCollectorInterface> mCollector;
+
     std::unique_ptr<InputDispatcherInterface> mDispatcher;
 };
 
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index da4e42f..492551e 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -94,6 +94,7 @@
 
 cc_library_static {
     name: "libinputdispatcher",
+    host_supported: true,
     defaults: [
         "inputflinger_defaults",
         "libinputdispatcher_defaults",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0cc7cfb..aa71e93 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4320,7 +4320,7 @@
     Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
                                                    args.pointerCount, args.pointerProperties);
     if (!motionCheck.ok()) {
-        LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
+        LOG(FATAL) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
         return;
     }
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 7388752..e03a773 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -205,6 +205,7 @@
     int32_t keyCode;
     int32_t keyMetaState;
     uint32_t policyFlags;
+    int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
 
     if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
                                   &policyFlags)) {
@@ -226,6 +227,7 @@
             // key repeat, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[*keyDownIndex].keyCode;
             downTime = mKeyDowns[*keyDownIndex].downTime;
+            flags = mKeyDowns[*keyDownIndex].flags;
         } else {
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -234,12 +236,14 @@
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
                 out += getDeviceContext().cancelTouch(when, readTime);
+                flags |= AKEY_EVENT_FLAG_KEEP_TOUCH_MODE;
             }
 
             KeyDown keyDown;
             keyDown.keyCode = keyCode;
             keyDown.scanCode = scanCode;
             keyDown.downTime = when;
+            keyDown.flags = flags;
             mKeyDowns.push_back(keyDown);
         }
     } else {
@@ -248,6 +252,7 @@
             // key up, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[*keyDownIndex].keyCode;
             downTime = mKeyDowns[*keyDownIndex].downTime;
+            flags = mKeyDowns[*keyDownIndex].flags;
             mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
         } else {
             // key was not actually down
@@ -281,9 +286,8 @@
 
     out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                    mSource, getDisplayId(), policyFlags,
-                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                                   AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
-                                   downTime));
+                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
+                                   keyCode, scanCode, keyMetaState, downTime));
     return out;
 }
 
@@ -410,7 +414,7 @@
         out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
                                        systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
                                        getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP,
-                                       AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+                                       mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
                                        mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
                                        mKeyDowns[i].downTime));
     }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index cd3d3c4..45fd68b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -57,6 +57,7 @@
         nsecs_t downTime{};
         int32_t keyCode{};
         int32_t scanCode{};
+        int32_t flags{};
     };
 
     uint32_t mSource{};
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 693ff06..b1e1aee 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -37,6 +37,7 @@
 cc_defaults {
     name: "libinputreporter_defaults",
     srcs: [":libinputreporter_sources"],
+    host_supported: true,
     shared_libs: [
         "liblog",
         "libutils",
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index a723636..f115fff 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -332,7 +332,7 @@
                       WithToolType(ToolType::FINGER)));
 }
 
-TEST_F(GestureConverterTest, Scroll_ClearsClassificationAndOffsetsAfterGesture) {
+TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
 
@@ -349,29 +349,70 @@
     Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
     ASSERT_EQ(1u, args.size());
-    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
-                AllOf(WithMotionClassification(MotionClassification::NONE),
-                      WithGestureScrollDistance(0, 0, EPSILON)));
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionClassification(MotionClassification::NONE));
 }
 
-TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAndOffsetsAfterGesture) {
+TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
 
-    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
-                         /* dy= */ 0);
+    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+    std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+
+    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+                         GESTURES_FLING_START);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+    // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+    // need to use another gesture type, like pinch.
+    Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+                         GESTURES_ZOOM_START);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+    ASSERT_FALSE(args.empty());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
+}
+
+TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                         /*dy=*/0);
     std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
 
     Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
 
-    Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ -5,
-                        /* dy= */ 10);
+    Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5,
+                        /*dy=*/10);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
     ASSERT_EQ(1u, args.size());
-    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
-                AllOf(WithMotionClassification(MotionClassification::NONE),
-                      WithGestureOffset(0, 0, EPSILON)));
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionClassification(MotionClassification::NONE));
+}
+
+TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsOffsetsAfterGesture) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
+                         /*dy=*/5);
+    std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+
+    // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+    // need to use another gesture type, like pinch.
+    Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+                         GESTURES_ZOOM_START);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+    ASSERT_FALSE(args.empty());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureOffset(0, 0, EPSILON));
 }
 
 TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
@@ -761,28 +802,52 @@
                       WithToolType(ToolType::FINGER)));
 }
 
-TEST_F(GestureConverterTest, Pinch_ClearsClassificationAndScaleFactorAfterGesture) {
+TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
 
-    Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+    Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
                          GESTURES_ZOOM_START);
     std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
 
     Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
-                          /* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
+                          /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
 
-    Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+    Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
                        GESTURES_ZOOM_END);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
 
     Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
     ASSERT_EQ(1u, args.size());
-    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
-                AllOf(WithMotionClassification(MotionClassification::NONE),
-                      WithGesturePinchScaleFactor(0, EPSILON)));
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionClassification(MotionClassification::NONE));
+}
+
+TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+                         GESTURES_ZOOM_START);
+    std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                          /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+
+    Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+                       GESTURES_ZOOM_END);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+
+    // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+    // need to use another gesture type, like scroll.
+    Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1,
+                          /*dy=*/0);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture);
+    ASSERT_FALSE(args.empty());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON));
 }
 
 TEST_F(GestureConverterTest, ResetWithButtonPressed) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 017f10b..3660f81 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1727,19 +1727,21 @@
         InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
         nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
         std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
-    MotionEvent event = MotionEventBuilder(action, source)
-                                .displayId(displayId)
-                                .eventTime(eventTime)
-                                .rawXCursorPosition(cursorPosition.x)
-                                .rawYCursorPosition(cursorPosition.y)
-                                .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
-                                                 .x(position.x)
-                                                 .y(position.y))
-                                .build();
+    MotionEventBuilder motionBuilder =
+            MotionEventBuilder(action, source)
+                    .displayId(displayId)
+                    .eventTime(eventTime)
+                    .rawXCursorPosition(cursorPosition.x)
+                    .rawYCursorPosition(cursorPosition.y)
+                    .pointer(
+                            PointerBuilder(/*id=*/0, ToolType::FINGER).x(position.x).y(position.y));
+    if (MotionEvent::getActionMasked(action) == ACTION_DOWN) {
+        motionBuilder.downTime(eventTime);
+    }
 
     // Inject event until dispatch out.
-    return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode, targetUid,
-                             policyFlags);
+    return injectMotionEvent(dispatcher, motionBuilder.build(), injectionTimeout, injectionMode,
+                             targetUid, policyFlags);
 }
 
 static InputEventInjectionResult injectMotionDown(
@@ -4921,6 +4923,25 @@
     mFakePolicy->assertUserActivityPoked();
 }
 
+TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+                                                             "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    window->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+    // Should have poked user activity
+    mFakePolicy->assertUserActivityPoked();
+}
+
 TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bfb371f..2df44ff 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3727,6 +3727,19 @@
     ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
 }
 
+TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
+    KeyboardInputMapper& mapper =
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    NotifyKeyArgs args;
+
+    // Key down
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 5301fe9..ed4829a 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -11,6 +11,7 @@
     name: "libsensorserviceaidl_fuzzer",
     defaults: [
         "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
     ],
     host_supported: true,
     static_libs: [
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5683a92..b355221 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -27,6 +27,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
+        "libtimestats_deps",
         "surfaceflinger_defaults",
     ],
     cflags: [
@@ -58,14 +59,12 @@
         "libGLESv2",
         "libgui",
         "libhidlbase",
-        "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
-        "libtimestats",
         "libui",
         "libinput",
         "libutils",
@@ -77,11 +76,13 @@
         "libcompositionengine",
         "libframetimeline",
         "libgui_aidl_static",
+        "liblayers_proto",
         "libperfetto_client_experimental",
         "librenderengine",
         "libscheduler",
         "libserviceutils",
         "libshaders",
+        "libtimestats",
         "libtonemap",
     ],
     header_libs: [
@@ -95,6 +96,7 @@
         "libcompositionengine",
         "librenderengine",
         "libserviceutils",
+        "libtimestats",
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
@@ -106,7 +108,6 @@
         "android.hardware.graphics.composer@2.4",
         "libpowermanager",
         "libhidlbase",
-        "libtimestats",
     ],
     // TODO (marissaw): this library is not used by surfaceflinger. This is here so
     // the library compiled in a way that is accessible to system partition when running
@@ -213,14 +214,12 @@
         "-DLOG_TAG=\"SurfaceFlinger\"",
     ],
     shared_libs: [
-        "android.frameworks.displayservice@1.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "libbinder",
         "libcutils",
-        "libdisplayservicehidl",
         "libhidlbase",
         "liblog",
         "libprocessgroup",
@@ -228,6 +227,8 @@
         "libutils",
     ],
     static_libs: [
+        "android.frameworks.displayservice@1.0",
+        "libdisplayservicehidl",
         "libserviceutils",
     ],
 }
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index f3a0186..702bd33 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -12,6 +12,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
+        "libtimestats_deps",
         "surfaceflinger_defaults",
     ],
     cflags: [
@@ -30,18 +31,18 @@
         "libbase",
         "libcutils",
         "libgui",
-        "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libprotobuf-cpp-lite",
         "libSurfaceFlingerProp",
-        "libtimestats",
         "libui",
         "libutils",
     ],
     static_libs: [
+        "liblayers_proto",
         "libmath",
         "librenderengine",
+        "libtimestats",
         "libtonemap",
         "libaidlcommonsupport",
         "libprocessgroup",
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
index cba1014..ef36234 100644
--- a/services/surfaceflinger/Display/PhysicalDisplay.h
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -21,9 +21,9 @@
 
 #include <binder/IBinder.h>
 #include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
 #include <utils/StrongPointer.h>
 
-#include "DisplayMap.h"
 #include "DisplaySnapshot.h"
 
 namespace android::display {
@@ -66,7 +66,7 @@
     DisplaySnapshot mSnapshot;
 };
 
-using PhysicalDisplays = PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>;
+using PhysicalDisplays = ui::PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>;
 
 // Combinator for ftl::Optional<PhysicalDisplayId>::and_then.
 constexpr auto getPhysicalDisplay(const PhysicalDisplays& displays) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ce05b38..ded91be 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -17,8 +17,9 @@
 #pragma once
 
 #include "ComposerHal.h"
+
 #include <ftl/shared_mutex.h>
-#include <ftl/small_map.h>
+#include <ui/DisplayMap.h>
 
 #include <functional>
 #include <optional>
@@ -272,9 +273,9 @@
     // Invalid displayId used as a key to mReaders when mSingleReader is true.
     static constexpr int64_t kSingleReaderKey = 0;
 
-    // TODO (b/256881188): Use display::PhysicalDisplayMap instead of hard-coded `3`
-    ftl::SmallMap<Display, ComposerClientWriter, 3> mWriters GUARDED_BY(mMutex);
-    ftl::SmallMap<Display, ComposerClientReader, 3> mReaders GUARDED_BY(mMutex);
+    ui::PhysicalDisplayMap<Display, ComposerClientWriter> mWriters GUARDED_BY(mMutex);
+    ui::PhysicalDisplayMap<Display, ComposerClientReader> mReaders GUARDED_BY(mMutex);
+
     // Protect access to mWriters and mReaders with a shared_mutex. Adding and
     // removing a display require exclusive access, since the iterator or the
     // writer/reader may be invalidated. Other calls need shared access while
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index ded734e..dcc29b9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -140,6 +140,10 @@
         janks.emplace_back("SurfaceFlinger Stuffing");
         jankType &= ~JankType::SurfaceFlingerStuffing;
     }
+    if (jankType & JankType::Dropped) {
+        janks.emplace_back("Dropped Frame");
+        jankType &= ~JankType::Dropped;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -264,6 +268,11 @@
         protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
         jankType &= ~JankType::SurfaceFlingerStuffing;
     }
+    if (jankType & JankType::Dropped) {
+        // Jank dropped does not append to other janks, it fully overrides.
+        protoJank |= FrameTimelineEvent::JANK_DROPPED;
+        jankType &= ~JankType::Dropped;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -365,8 +374,7 @@
 std::optional<int32_t> SurfaceFrame::getJankType() const {
     std::scoped_lock lock(mMutex);
     if (mPresentState == PresentState::Dropped) {
-        // Return no jank if it's a dropped frame since we cannot attribute a jank to a it.
-        return JankType::None;
+        return JankType::Dropped;
     }
     if (mActuals.presentTime == 0) {
         // Frame hasn't been presented yet.
@@ -503,7 +511,8 @@
         // We classify prediction expired as AppDeadlineMissed as the
         // TokenManager::kMaxTokens we store is large enough to account for a
         // reasonable app, so prediction expire would mean a huge scheduling delay.
-        mJankType = JankType::AppDeadlineMissed;
+        mJankType = mPresentState != PresentState::Presented ? JankType::Dropped
+                                                             : JankType::AppDeadlineMissed;
         deadlineDelta = -1;
         return;
     }
@@ -594,17 +603,17 @@
             mJankType |= displayFrameJankType;
         }
     }
+    if (mPresentState != PresentState::Presented) {
+        mJankType = JankType::Dropped;
+        // Since frame was not presented, lets drop any present value
+        mActuals.presentTime = 0;
+    }
 }
 
 void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
                              nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
     std::scoped_lock lock(mMutex);
 
-    if (mPresentState != PresentState::Presented) {
-        // No need to update dropped buffers
-        return;
-    }
-
     mActuals.presentTime = presentTime;
     nsecs_t deadlineDelta = 0;
 
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 218a64a..6502f36 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -19,6 +19,9 @@
 #include <sstream>
 
 #include <gui/DisplayInfo.h>
+#include <ui/DisplayMap.h>
+#include <ui/LayerStack.h>
+#include <ui/Transform.h>
 
 namespace android::surfaceflinger::frontend {
 
@@ -44,4 +47,6 @@
     }
 };
 
+using DisplayInfos = ui::DisplayMap<ui::LayerStack, DisplayInfo>;
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 985c6f9..96ff70c 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -19,28 +19,26 @@
 #undef LOG_TAG
 #define LOG_TAG "LayerSnapshotBuilder"
 
-#include "LayerSnapshotBuilder.h"
-#include <gui/TraceUtils.h>
-#include <ui/FloatRect.h>
-
 #include <numeric>
 #include <optional>
 
+#include <ftl/small_map.h>
 #include <gui/TraceUtils.h>
+#include <ui/FloatRect.h>
+
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/Hal.h"
 #include "LayerLog.h"
 #include "LayerSnapshotBuilder.h"
 #include "TimeStats/TimeStats.h"
-#include "ftl/small_map.h"
 
 namespace android::surfaceflinger::frontend {
 
 using namespace ftl::flag_operators;
 
 namespace {
-FloatRect getMaxDisplayBounds(
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+
+FloatRect getMaxDisplayBounds(const DisplayInfos& displays) {
     const ui::Size maxSize = [&displays] {
         if (displays.empty()) return ui::Size{5000, 5000};
 
@@ -668,8 +666,7 @@
 }
 
 // TODO (b/259407931): Remove.
-uint32_t getPrimaryDisplayRotationFlags(
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+uint32_t getPrimaryDisplayRotationFlags(const DisplayInfos& displays) {
     for (auto& [_, display] : displays) {
         if (display.isPrimary) {
             return display.rotationFlags;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 148c98e..3f33ab8 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include "Display/DisplayMap.h"
 #include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerLifecycleManager.h"
 #include "LayerHierarchy.h"
@@ -45,7 +44,7 @@
         const LayerLifecycleManager& layerLifecycleManager;
         ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE;
         bool includeMetadata = false;
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays;
+        const DisplayInfos& displays;
         // Set to true if there were display changes since last update.
         bool displayChanges = false;
         const renderengine::ShadowSettings& globalShadowSettings;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 3472d20..e61916c 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -446,7 +446,7 @@
 }
 
 google::protobuf::RepeatedPtrField<DisplayProto> LayerProtoHelper::writeDisplayInfoToProto(
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos) {
+        const frontend::DisplayInfos& displayInfos) {
     google::protobuf::RepeatedPtrField<DisplayProto> displays;
     displays.Reserve(displayInfos.size());
     for (const auto& [layerStack, displayInfo] : displayInfos) {
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index b84a49b..346685f 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -26,6 +26,8 @@
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <cstdint>
+
+#include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerSnapshot.h"
 
@@ -65,15 +67,15 @@
                                      const frontend::RequestedLayerState& requestedState,
                                      const frontend::LayerSnapshot& snapshot, uint32_t traceFlags);
     static google::protobuf::RepeatedPtrField<DisplayProto> writeDisplayInfoToProto(
-            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos);
+            const frontend::DisplayInfos&);
 };
 
 class LayerProtoFromSnapshotGenerator {
 public:
-    LayerProtoFromSnapshotGenerator(
-            const frontend::LayerSnapshotBuilder& snapshotBuilder,
-            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
-            const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags)
+    LayerProtoFromSnapshotGenerator(const frontend::LayerSnapshotBuilder& snapshotBuilder,
+                                    const frontend::DisplayInfos& displayInfos,
+                                    const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers,
+                                    uint32_t traceFlags)
           : mSnapshotBuilder(snapshotBuilder),
             mLegacyLayers(legacyLayers),
             mDisplayInfos(displayInfos),
@@ -88,7 +90,7 @@
 
     const frontend::LayerSnapshotBuilder& mSnapshotBuilder;
     const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers;
-    const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& mDisplayInfos;
+    const frontend::DisplayInfos& mDisplayInfos;
     uint32_t mTraceFlags;
     LayersProto mLayersProto;
     // winscope expects all the layers, so provide a snapshot even if it not currently drawing
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f1fd6db..607bec2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -148,7 +148,8 @@
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
                             bufferStatus);
 
-        sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+        sk_sp<SkSurface> surface = SkSurfaces::Raster(
+                SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
         SkCanvas* canvas = surface->getCanvas();
         canvas->setMatrix(canvasTransform);
 
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 92c2189..badbf53 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -25,7 +25,7 @@
 namespace android::scheduler {
 
 struct ISchedulerCallback {
-    virtual void setVsyncEnabled(PhysicalDisplayId, bool) = 0;
+    virtual void requestHardwareVsync(PhysicalDisplayId, bool enabled) = 0;
     virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 9319543..918d401 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -31,6 +31,7 @@
 #include <gui/TraceUtils.h>
 #include <gui/WindowInfo.h>
 #include <system/window.h>
+#include <ui/DisplayMap.h>
 #include <utils/Timers.h>
 
 #include <FrameTimeline/FrameTimeline.h>
@@ -44,7 +45,6 @@
 #include <numeric>
 
 #include "../Layer.h"
-#include "Display/DisplayMap.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
 #include "FrontEnd/LayerHandle.h"
@@ -114,8 +114,12 @@
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
-    registerDisplayInternal(displayId, std::move(selectorPtr),
-                            std::make_shared<VsyncSchedule>(displayId, mFeatures));
+    auto schedulePtr = std::make_shared<VsyncSchedule>(displayId, mFeatures,
+                                                       [this](PhysicalDisplayId id, bool enable) {
+                                                           onHardwareVsyncRequest(id, enable);
+                                                       });
+
+    registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
 }
 
 void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
@@ -123,14 +127,22 @@
                                         VsyncSchedulePtr schedulePtr) {
     demotePacesetterDisplay();
 
-    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
-    {
+    auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
         std::scoped_lock lock(mDisplayLock);
-        mDisplays.emplace_or_replace(displayId, std::move(selectorPtr), std::move(schedulePtr));
+        const bool isNew = mDisplays
+                                   .emplace_or_replace(displayId, std::move(selectorPtr),
+                                                       std::move(schedulePtr))
+                                   .second;
 
-        pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
-    }
+        return std::make_pair(promotePacesetterDisplayLocked(), isNew);
+    }();
+
     applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
+
+    // Disable hardware VSYNC if the registration is new, as opposed to a renewal.
+    if (isNew) {
+        onHardwareVsyncRequest(displayId, false);
+    }
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
@@ -407,13 +419,13 @@
 void Scheduler::enableHardwareVsync(PhysicalDisplayId id) {
     auto schedule = getVsyncSchedule(id);
     LOG_ALWAYS_FATAL_IF(!schedule);
-    schedule->enableHardwareVsync(mSchedulerCallback);
+    schedule->enableHardwareVsync();
 }
 
 void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) {
     auto schedule = getVsyncSchedule(id);
     LOG_ALWAYS_FATAL_IF(!schedule);
-    schedule->disableHardwareVsync(mSchedulerCallback, disallow);
+    schedule->disableHardwareVsync(disallow);
 }
 
 void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) {
@@ -440,12 +452,32 @@
             refreshRate = display.selectorPtr->getActiveMode().modePtr->getFps();
         }
         if (refreshRate->isValid()) {
-            display.schedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
-                                                       false /* force */);
+            constexpr bool kForce = false;
+            display.schedulePtr->startPeriodTransition(refreshRate->getPeriod(), kForce);
         }
     }
 }
 
+void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) {
+    static const auto& whence = __func__;
+    ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
+
+    // On main thread to serialize reads/writes of pending hardware VSYNC state.
+    static_cast<void>(
+            schedule([=]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
+
+                if (const auto displayOpt = mDisplays.get(id)) {
+                    auto& display = displayOpt->get();
+                    display.schedulePtr->setPendingHardwareVsyncState(enabled);
+
+                    if (display.powerMode != hal::PowerMode::OFF) {
+                        mSchedulerCallback.requestHardwareVsync(id, enabled);
+                    }
+                }
+            }));
+}
+
 void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) {
     std::scoped_lock lock(mDisplayLock);
     ftl::FakeGuard guard(kMainThreadContext);
@@ -491,18 +523,17 @@
         ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
         return false;
     }
-    return schedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
-                                     hwcVsyncPeriod);
+    return schedule->addResyncSample(TimePoint::fromNs(timestamp), hwcVsyncPeriod);
 }
 
 void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
     auto schedule = getVsyncSchedule(id);
     LOG_ALWAYS_FATAL_IF(!schedule);
-    const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
-    if (needMoreSignals) {
-        schedule->enableHardwareVsync(mSchedulerCallback);
+    if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
+        schedule->enableHardwareVsync();
     } else {
-        schedule->disableHardwareVsync(mSchedulerCallback, false /* disallow */);
+        constexpr bool kDisallow = false;
+        schedule->disableHardwareVsync(kDisallow);
     }
 }
 
@@ -566,9 +597,13 @@
     }
     {
         std::scoped_lock lock(mDisplayLock);
-        auto vsyncSchedule = getVsyncScheduleLocked(id);
-        LOG_ALWAYS_FATAL_IF(!vsyncSchedule);
-        vsyncSchedule->getController().setDisplayPowerMode(powerMode);
+
+        const auto displayOpt = mDisplays.get(id);
+        LOG_ALWAYS_FATAL_IF(!displayOpt);
+        auto& display = displayOpt->get();
+
+        display.powerMode = powerMode;
+        display.schedulePtr->getController().setDisplayPowerMode(powerMode);
     }
     if (!isPacesetter) return;
 
@@ -626,7 +661,7 @@
         ftl::FakeGuard guard(kMainThreadContext);
         for (const auto& [_, display] : mDisplays) {
             constexpr bool kDisallow = false;
-            display.schedulePtr->disableHardwareVsync(mSchedulerCallback, kDisallow);
+            display.schedulePtr->disableHardwareVsync(kDisallow);
         }
     }
 
@@ -745,8 +780,8 @@
         newVsyncSchedulePtr = pacesetter.schedulePtr;
 
         const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getFps();
-        newVsyncSchedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
-                                                   true /* force */);
+        constexpr bool kForce = true;
+        newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce);
     }
     return newVsyncSchedulePtr;
 }
@@ -846,7 +881,7 @@
     ATRACE_CALL();
 
     using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
-    display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
+    ui::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
     const auto globalSignals = makeGlobalSignals();
     Fps pacesetterFps;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f13c878..a1354fa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -38,8 +38,8 @@
 #include <scheduler/Time.h>
 #include <scheduler/VsyncConfig.h>
 #include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
 
-#include "Display/DisplayMap.h"
 #include "Display/DisplayModeRequest.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
@@ -317,6 +317,9 @@
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
 
+    // VsyncSchedule delegate.
+    void onHardwareVsyncRequest(PhysicalDisplayId, bool enable);
+
     void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable,
                                      std::optional<Fps> refreshRate = std::nullopt)
             REQUIRES(kMainThreadContext, mDisplayLock);
@@ -371,7 +374,7 @@
         }
     };
 
-    using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
+    using DisplayModeChoiceMap = ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
 
     // See mDisplayLock for thread safety.
     DisplayModeChoiceMap chooseDisplayModes() const
@@ -430,12 +433,14 @@
         // Effectively const except in move constructor.
         RefreshRateSelectorPtr selectorPtr;
         VsyncSchedulePtr schedulePtr;
+
+        hal::PowerMode powerMode = hal::PowerMode::OFF;
     };
 
     using DisplayRef = std::reference_wrapper<Display>;
     using ConstDisplayRef = std::reference_wrapper<const Display>;
 
-    display::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays GUARDED_BY(mDisplayLock)
+    ui::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays GUARDED_BY(mDisplayLock)
             GUARDED_BY(kMainThreadContext);
 
     ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 84671ae..ff3f29d 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -22,7 +22,6 @@
 
 #include "VsyncSchedule.h"
 
-#include "ISchedulerCallback.h"
 #include "Utils/Dumper.h"
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
@@ -54,8 +53,10 @@
     VSyncCallbackRegistration mRegistration;
 };
 
-VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features)
+VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features,
+                             RequestHardwareVsync requestHardwareVsync)
       : mId(id),
+        mRequestHardwareVsync(std::move(requestHardwareVsync)),
         mTracker(createTracker(id)),
         mDispatch(createDispatch(mTracker)),
         mController(createController(id, *mTracker, features)),
@@ -64,8 +65,9 @@
                         : nullptr) {}
 
 VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, TrackerPtr tracker, DispatchPtr dispatch,
-                             ControllerPtr controller)
+                             ControllerPtr controller, RequestHardwareVsync requestHardwareVsync)
       : mId(id),
+        mRequestHardwareVsync(std::move(requestHardwareVsync)),
         mTracker(std::move(tracker)),
         mDispatch(std::move(dispatch)),
         mController(std::move(controller)) {}
@@ -135,14 +137,13 @@
     return reactor;
 }
 
-void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period, bool force) {
+void VsyncSchedule::startPeriodTransition(Period period, bool force) {
     std::lock_guard<std::mutex> lock(mHwVsyncLock);
     mController->startPeriodTransition(period.ns(), force);
-    enableHardwareVsyncLocked(callback);
+    enableHardwareVsyncLocked();
 }
 
-bool VsyncSchedule::addResyncSample(ISchedulerCallback& callback, TimePoint timestamp,
-                                    ftl::Optional<Period> hwcVsyncPeriod) {
+bool VsyncSchedule::addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod) {
     bool needsHwVsync = false;
     bool periodFlushed = false;
     {
@@ -154,31 +155,32 @@
         }
     }
     if (needsHwVsync) {
-        enableHardwareVsync(callback);
+        enableHardwareVsync();
     } else {
-        disableHardwareVsync(callback, false /* disallow */);
+        constexpr bool kDisallow = false;
+        disableHardwareVsync(kDisallow);
     }
     return periodFlushed;
 }
 
-void VsyncSchedule::enableHardwareVsync(ISchedulerCallback& callback) {
+void VsyncSchedule::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHwVsyncLock);
-    enableHardwareVsyncLocked(callback);
+    enableHardwareVsyncLocked();
 }
 
-void VsyncSchedule::enableHardwareVsyncLocked(ISchedulerCallback& callback) {
+void VsyncSchedule::enableHardwareVsyncLocked() {
     if (mHwVsyncState == HwVsyncState::Disabled) {
         getTracker().resetModel();
-        callback.setVsyncEnabled(mId, true);
+        mRequestHardwareVsync(mId, true);
         mHwVsyncState = HwVsyncState::Enabled;
     }
 }
 
-void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) {
+void VsyncSchedule::disableHardwareVsync(bool disallow) {
     std::lock_guard<std::mutex> lock(mHwVsyncLock);
     switch (mHwVsyncState) {
         case HwVsyncState::Enabled:
-            callback.setVsyncEnabled(mId, false);
+            mRequestHardwareVsync(mId, false);
             [[fallthrough]];
         case HwVsyncState::Disabled:
             mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 763d058..58e0432 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -16,11 +16,13 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 
 #include <ThreadContext.h>
 #include <android-base/thread_annotations.h>
+#include <ThreadContext.h>
 #include <ftl/enum.h>
 #include <ftl/optional.h>
 #include <scheduler/Features.h>
@@ -38,8 +40,6 @@
 
 namespace android::scheduler {
 
-struct ISchedulerCallback;
-
 // TODO(b/185535769): Rename classes, and remove aliases.
 class VSyncDispatch;
 class VSyncTracker;
@@ -51,7 +51,9 @@
 // Schedule that synchronizes to hardware VSYNC of a physical display.
 class VsyncSchedule {
 public:
-    VsyncSchedule(PhysicalDisplayId, FeatureFlags);
+    using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
+
+    VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
     ~VsyncSchedule();
 
     Period period() const;
@@ -64,13 +66,12 @@
     // \param [in] period   The period that the system is changing into.
     // \param [in] force    True to force a transition even if it is not a
     //                      change.
-    void startPeriodTransition(ISchedulerCallback&, Period period, bool force);
+    void startPeriodTransition(Period period, bool force);
 
     // Pass a VSYNC sample to VsyncController. Return true if
     // VsyncController detected that the VSYNC period changed. Enable or disable
     // hardware VSYNCs depending on whether more samples are needed.
-    bool addResyncSample(ISchedulerCallback&, TimePoint timestamp,
-                         ftl::Optional<Period> hwcVsyncPeriod);
+    bool addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod);
 
     // TODO(b/185535769): Hide behind API.
     const VsyncTracker& getTracker() const { return *mTracker; }
@@ -89,12 +90,12 @@
 
     // Turn on hardware VSYNCs, unless mHwVsyncState is Disallowed, in which
     // case this call is ignored.
-    void enableHardwareVsync(ISchedulerCallback&) EXCLUDES(mHwVsyncLock);
+    void enableHardwareVsync() EXCLUDES(mHwVsyncLock);
 
     // Disable hardware VSYNCs. If `disallow` is true, future calls to
     // enableHardwareVsync are ineffective until isHardwareVsyncAllowed is
     // called with `makeAllowed` set to true.
-    void disableHardwareVsync(ISchedulerCallback&, bool disallow) EXCLUDES(mHwVsyncLock);
+    void disableHardwareVsync(bool disallow) EXCLUDES(mHwVsyncLock);
 
     // If true, enableHardwareVsync can enable hardware VSYNC (if not already
     // enabled). If false, enableHardwareVsync does nothing.
@@ -107,8 +108,11 @@
 protected:
     using ControllerPtr = std::unique_ptr<VsyncController>;
 
+    static void NoOpRequestHardwareVsync(PhysicalDisplayId, bool) {}
+
     // For tests.
-    VsyncSchedule(PhysicalDisplayId, TrackerPtr, DispatchPtr, ControllerPtr);
+    VsyncSchedule(PhysicalDisplayId, TrackerPtr, DispatchPtr, ControllerPtr,
+                  RequestHardwareVsync = NoOpRequestHardwareVsync);
 
 private:
     friend class TestableScheduler;
@@ -120,7 +124,7 @@
     static DispatchPtr createDispatch(TrackerPtr);
     static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
 
-    void enableHardwareVsyncLocked(ISchedulerCallback&) REQUIRES(mHwVsyncLock);
+    void enableHardwareVsyncLocked() REQUIRES(mHwVsyncLock);
 
     mutable std::mutex mHwVsyncLock;
     enum class HwVsyncState {
@@ -147,6 +151,7 @@
     using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
 
     const PhysicalDisplayId mId;
+    const RequestHardwareVsync mRequestHardwareVsync;
     const TrackerPtr mTracker;
     const DispatchPtr mDispatch;
     const ControllerPtr mController;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h
index c64a3cd..6ca4e85 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h
@@ -18,17 +18,17 @@
 
 #include <cstdint>
 
+#include <ftl/mixins.h>
+
 namespace android {
 
-// TODO(b/185536303): Import StrongTyping.h into FTL so it can be used here.
-
 // Sequential frame identifier, also known as FrameTimeline token.
-struct VsyncId {
-    int64_t value = -1;
+//
+// TODO(b/241285191): Pull to <gui/FrameTimelineInfo.h> and use VsyncId over int64_t everywhere.
+struct VsyncId : ftl::DefaultConstructible<VsyncId, int64_t, -1>,
+                 ftl::Incrementable<VsyncId>,
+                 ftl::Equatable<VsyncId> {
+    using DefaultConstructible::DefaultConstructible;
 };
 
-inline bool operator==(VsyncId lhs, VsyncId rhs) {
-    return lhs.value == rhs.value;
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2ac1db9..a32da6c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -116,7 +116,6 @@
 #include "Client.h"
 #include "ClientCache.h"
 #include "Colorizer.h"
-#include "Display/DisplayMap.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/FramebufferSurface.h"
@@ -376,6 +375,7 @@
 }
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
+    ATRACE_CALL();
     ALOGI("SurfaceFlinger is starting");
 
     hasSyncFramework = running_without_sync_framework(true);
@@ -689,7 +689,7 @@
 
     // wait patiently for the window manager death
     const String16 name("window");
-    mWindowManager = defaultServiceManager()->getService(name);
+    mWindowManager = defaultServiceManager()->waitForService(name);
     if (mWindowManager != 0) {
         mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this));
     }
@@ -703,7 +703,7 @@
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
-    sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+    sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
 
     static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
         if (input == nullptr) {
@@ -802,6 +802,7 @@
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
 void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
+    ATRACE_CALL();
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
     addTransactionReadyFilters();
@@ -2140,26 +2141,6 @@
     }
 }
 
-void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) {
-    const char* const whence = __func__;
-    ATRACE_FORMAT("%s (%d) for %" PRIu64, whence, enabled, id.value);
-
-    // On main thread to avoid race conditions with display power state.
-    static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
-        {
-            ftl::FakeGuard guard(kMainThreadContext);
-            if (auto schedule = mScheduler->getVsyncSchedule(id)) {
-                schedule->setPendingHardwareVsyncState(enabled);
-            }
-        }
-
-        ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value);
-        if (const auto display = getDisplayDeviceLocked(id); display && display->isPoweredOn()) {
-            setHWCVsyncEnabled(id, enabled);
-        }
-    }));
-}
-
 bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
     const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
     return isThreeVsyncsAhead ||
@@ -2384,7 +2365,7 @@
     mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime
                                                           : calculateExpectedPresentTime(frameTime);
 
-    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId.value,
+    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(vsyncId),
                   ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
                   mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
 
@@ -2498,7 +2479,7 @@
     // Composite if transactions were committed, or if requested by HWC.
     bool mustComposite = mMustComposite.exchange(false);
     {
-        mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(),
+        mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), frameTime.ns(),
                                     Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
         const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
@@ -2506,8 +2487,9 @@
         if (flushTransactions) {
             updates = flushLifecycleUpdates();
             if (mTransactionTracing) {
-                mTransactionTracing->addCommittedTransactions(vsyncId.value, frameTime.ns(),
-                                                              updates, mFrontEndDisplayInfos,
+                mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId),
+                                                              frameTime.ns(), updates,
+                                                              mFrontEndDisplayInfos,
                                                               mFrontEndDisplayInfosChanged);
             }
         }
@@ -2551,7 +2533,7 @@
 
     if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and tracing should only be enabled for debugging.
-        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
     }
     mLastCommittedVsyncId = vsyncId;
 
@@ -2562,7 +2544,7 @@
 
 void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId.value);
+    ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
 
     compositionengine::CompositionRefreshArgs refreshArgs;
     const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
@@ -2654,8 +2636,9 @@
     // the scheduler.
     const auto presentTime = systemTime();
 
-    std::vector<std::pair<Layer*, LayerFE*>> layers =
-            moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/false, vsyncId.value);
+    constexpr bool kCursorOnly = false;
+    const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly);
+
     mCompositionEngine->present(refreshArgs);
     moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 
@@ -2733,7 +2716,7 @@
     mLayersWithQueuedFrames.clear();
     if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and should only be used for debugging.
-        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
     }
 
     if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2880,7 +2863,7 @@
     const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
                                             presentLatency.ns());
 
-    display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
+    ui::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
     {
         if (!mLayersWithBuffersRemoved.empty() || mNumTrustedPresentationListeners > 0) {
             Mutex::Autolock lock(mStateLock);
@@ -3779,7 +3762,8 @@
             mWindowInfosListenerInvoker
                     ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
                                                                 std::move(displayInfos),
-                                                                vsyncId.value, frameTime.ns()},
+                                                                ftl::to_underlying(vsyncId),
+                                                                frameTime.ns()},
                                          std::move(
                                                  inputWindowCommands.windowInfosReportedListeners),
                                          /* forceImmediateCall= */ visibleWindowsChanged ||
@@ -3869,11 +3853,17 @@
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
-    auto layers = moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/true, 0);
+
+    constexpr bool kCursorOnly = true;
+    const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly);
     mCompositionEngine->updateCursorAsync(refreshArgs);
     moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 }
 
+void SurfaceFlinger::requestHardwareVsync(PhysicalDisplayId displayId, bool enable) {
+    getHwComposer().setVsyncEnabled(displayId, enable ? hal::Vsync::ENABLE : hal::Vsync::DISABLE);
+}
+
 void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
     if (mBootStage != BootStage::FINISHED) {
         ALOGV("Currently in the boot stage, skipping display mode changes");
@@ -3957,8 +3947,6 @@
                                              static_cast<ISchedulerCallback&>(*this), features,
                                              std::move(modulatorPtr));
     mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
-
-    setVsyncEnabled(display->getPhysicalId(), false);
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
@@ -4434,7 +4422,7 @@
 
 bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
     const auto prediction =
-            mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId.value);
+            mFrameTimeline->getTokenManager()->getPredictionsForToken(ftl::to_underlying(vsyncId));
     if (!prediction) {
         return false;
     }
@@ -5519,11 +5507,14 @@
 
         getHwComposer().setPowerMode(displayId, mode);
         if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
-            setHWCVsyncEnabled(displayId,
-                               mScheduler->getVsyncSchedule(displayId)
-                                       ->getPendingHardwareVsyncState());
+            const bool enable =
+                    mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
+            requestHardwareVsync(displayId, enable);
+
             mScheduler->enableSyntheticVsync(false);
-            mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
+
+            constexpr bool kAllowToEnable = true;
+            mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, refreshRate);
         }
 
         mVisibleRegionsDirty = true;
@@ -5547,10 +5538,10 @@
             }
         }
 
-        // Make sure HWVsync is disabled before turning off the display
-        setHWCVsyncEnabled(displayId, false);
-
+        // Disable VSYNC before turning off the display.
+        requestHardwareVsync(displayId, false);
         getHwComposer().setPowerMode(displayId, mode);
+
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
@@ -5578,9 +5569,10 @@
     if (displayId == mActiveDisplayId) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
-        mScheduler->setDisplayPowerMode(displayId, mode);
     }
 
+    mScheduler->setDisplayPowerMode(displayId, mode);
+
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
 }
 
@@ -6152,7 +6144,7 @@
     result.append("Window Infos:\n");
     auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
     StringAppendF(&result, "  max send vsync id: %" PRId64 "\n",
-                  windowInfosDebug.maxSendDelayVsyncId.value);
+                  ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
     StringAppendF(&result, "  max send delay (ns): %" PRId64 " ns\n",
                   windowInfosDebug.maxSendDelayDuration);
     StringAppendF(&result, "  unsent messages: %" PRIu32 "\n",
@@ -6472,13 +6464,16 @@
                     ALOGD("LayerTracing enabled");
                     tracingEnabledChanged = mLayerTracing.enable();
                     if (tracingEnabledChanged) {
-                        int64_t startingTime =
-                                (fixedStartingTime) ? fixedStartingTime : systemTime();
+                        const TimePoint startingTime = fixedStartingTime
+                                ? TimePoint::fromNs(fixedStartingTime)
+                                : TimePoint::now();
+
                         mScheduler
-                                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
-                                                   kMainThreadContext) {
-                                    addToLayerTracing(true /* visibleRegionDirty */, startingTime,
-                                                      mLastCommittedVsyncId.value);
+                                ->schedule([this, startingTime]() FTL_FAKE_GUARD(
+                                                   mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                                    constexpr bool kVisibleRegionDirty = true;
+                                    addToLayerTracing(kVisibleRegionDirty, startingTime,
+                                                      mLastCommittedVsyncId);
                                 })
                                 .wait();
                     }
@@ -8128,7 +8123,7 @@
 
 void SurfaceFlinger::moveSnapshotsFromCompositionArgs(
         compositionengine::CompositionRefreshArgs& refreshArgs,
-        std::vector<std::pair<Layer*, LayerFE*>>& layers) {
+        const std::vector<std::pair<Layer*, LayerFE*>>& layers) {
     if (mLayerLifecycleManagerEnabled) {
         std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots =
                 mLayerSnapshotBuilder.getSnapshots();
@@ -8145,7 +8140,7 @@
 }
 
 std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToCompositionArgs(
-        compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) {
+        compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly) {
     std::vector<std::pair<Layer*, LayerFE*>> layers;
     if (mLayerLifecycleManagerEnabled) {
         nsecs_t currentTime = systemTime();
@@ -8345,7 +8340,7 @@
     return update;
 }
 
-void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, TimePoint time, VsyncId vsyncId) {
     const uint32_t tracingFlags = mLayerTracing.getFlags();
     LayersProto layers(dumpDrawingStateProto(tracingFlags));
     if (tracingFlags & LayerTracing::TRACE_EXTRA) {
@@ -8356,7 +8351,8 @@
         dumpHwc(hwcDump);
     }
     auto displays = dumpDisplayProto();
-    mLayerTracing.notify(visibleRegionDirty, time, vsyncId, &layers, std::move(hwcDump), &displays);
+    mLayerTracing.notify(visibleRegionDirty, time.ns(), ftl::to_underlying(vsyncId), &layers,
+                         std::move(hwcDump), &displays);
 }
 
 // gui::ISurfaceComposer
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0bc506f..b7d2047 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -43,6 +43,7 @@
 #include <renderengine/LayerSettings.h>
 #include <serviceutils/PriorityDumper.h>
 #include <system/graphics.h>
+#include <ui/DisplayMap.h>
 #include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
 #include <ui/Size.h>
@@ -62,7 +63,6 @@
 #include <scheduler/interface/ICompositor.h>
 #include <ui/FenceResult.h>
 
-#include "Display/DisplayMap.h"
 #include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
@@ -637,10 +637,7 @@
     void sample() override;
 
     // ISchedulerCallback overrides:
-
-    // Toggles hardware VSYNC by calling into HWC.
-    // TODO(b/241286146): Rename for self-explanatory API.
-    void setVsyncEnabled(PhysicalDisplayId, bool) override;
+    void requestHardwareVsync(PhysicalDisplayId, bool) override;
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
     void kernelTimerChanged(bool expired) override;
     void triggerOnFrameRateOverridesChanged() override;
@@ -710,10 +707,9 @@
     void updateLayerGeometry();
     void updateLayerMetadataSnapshot();
     std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
-            compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly,
-            int64_t vsyncId);
+            compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
     void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
-                                          std::vector<std::pair<Layer*, LayerFE*>>& layers);
+                                          const std::vector<std::pair<Layer*, LayerFE*>>& layers);
     bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
                                     bool transactionsFlushed, bool& out)
             REQUIRES(kMainThreadContext);
@@ -997,11 +993,6 @@
      */
     nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
 
-    void setHWCVsyncEnabled(PhysicalDisplayId id, bool enabled) {
-        hal::Vsync halState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-        getHwComposer().setVsyncEnabled(id, halState);
-    }
-
     using FenceTimePtr = std::shared_ptr<FenceTime>;
 
     bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
@@ -1086,7 +1077,7 @@
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
     google::protobuf::RepeatedPtrField<DisplayProto> dumpDisplayProto() const;
-    void addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId)
+    void addToLayerTracing(bool visibleRegionDirty, TimePoint, VsyncId)
             REQUIRES(kMainThreadContext);
 
     // Dumps state from HW Composer
@@ -1224,7 +1215,7 @@
     // never removed, so take precedence over external and virtual displays.
     //
     // May be read from any thread, but must only be written from the main thread.
-    display::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+    ui::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
 
     display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
 
@@ -1442,7 +1433,7 @@
     std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
 
     TransactionHandler mTransactionHandler;
-    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
+    ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
     bool mFrontEndDisplayInfosChanged = false;
 
     // WindowInfo ids visible during the last commit.
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 4686eed..c3141be 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -7,14 +7,9 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library {
-    name: "libtimestats",
-    srcs: [
-        "TimeStats.cpp",
-    ],
-    header_libs: [
-        "libscheduler_headers",
-    ],
+cc_defaults {
+    name: "libtimestats_deps",
+
     shared_libs: [
         "android.hardware.graphics.composer@2.4",
         "libbase",
@@ -22,17 +17,34 @@
         "liblog",
         "libprotobuf-cpp-lite",
         "libtimestats_atoms_proto",
-        "libtimestats_proto",
         "libui",
         "libutils",
     ],
+
+    static_libs: [
+        "libtimestats_proto",
+    ],
+
+    export_static_lib_headers: [
+        "libtimestats_proto",
+    ],
+}
+
+cc_library {
+    name: "libtimestats",
+    defaults: [
+        "libtimestats_deps",
+    ],
+    srcs: [
+        "TimeStats.cpp",
+    ],
+    header_libs: [
+        "libscheduler_headers",
+    ],
     export_include_dirs: ["."],
     export_header_lib_headers: [
         "libscheduler_headers",
     ],
-    export_shared_lib_headers: [
-        "libtimestats_proto",
-    ],
     cppflags: [
         "-Wall",
         "-Werror",
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index cf1ca65..cbbcb91 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -115,7 +115,7 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     result.append("Jank payload for this layer:\n");
     result.append(jankPayload.toString());
-    result.append("SetFrateRate vote for this layer:\n");
+    result.append("SetFrameRate vote for this layer:\n");
     result.append(setFrameRateVote.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 0694180..dafdc8a 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -601,7 +601,7 @@
 
 void TransactionProtoParser::fromProto(
         const google::protobuf::RepeatedPtrField<proto::DisplayInfo>& proto,
-        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& outDisplayInfos) {
+        frontend::DisplayInfos& outDisplayInfos) {
     outDisplayInfos.clear();
     for (const proto::DisplayInfo& displayInfo : proto) {
         outDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(displayInfo.layer_stack()),
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index d6c98e1..457c3be 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -18,9 +18,8 @@
 #include <gui/fake/BufferData.h>
 #include <layerproto/TransactionProto.h>
 #include <utils/RefBase.h>
-#include "Display/DisplayMap.h"
-#include "FrontEnd/DisplayInfo.h"
 
+#include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerCreationArgs.h"
 #include "TransactionState.h"
 
@@ -56,9 +55,8 @@
     void fromProto(const proto::LayerCreationArgs&, LayerCreationArgs& outArgs);
     std::unique_ptr<FlingerDataMapper> mMapper;
     static frontend::DisplayInfo fromProto(const proto::DisplayInfo&);
-    static void fromProto(
-            const google::protobuf::RepeatedPtrField<proto::DisplayInfo>&,
-            display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& outDisplayInfos);
+    static void fromProto(const google::protobuf::RepeatedPtrField<proto::DisplayInfo>&,
+                          frontend::DisplayInfos& outDisplayInfos);
 
 private:
     proto::DisplayState toProto(const DisplayState&);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 87a633f..632de01 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -92,10 +92,10 @@
     mTransactionQueue.push(state);
 }
 
-void TransactionTracing::addCommittedTransactions(
-        int64_t vsyncId, nsecs_t commitTime, frontend::Update& newUpdate,
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
-        bool displayInfoChanged) {
+void TransactionTracing::addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime,
+                                                  frontend::Update& newUpdate,
+                                                  const frontend::DisplayInfos& displayInfos,
+                                                  bool displayInfoChanged) {
     CommittedUpdates update;
     update.vsyncId = vsyncId;
     update.timestamp = commitTime;
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index f27e7a9..0e56627 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,7 +25,6 @@
 #include <mutex>
 #include <thread>
 
-#include "Display/DisplayMap.h"
 #include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/Update.h"
@@ -59,10 +58,8 @@
     ~TransactionTracing();
 
     void addQueuedTransaction(const TransactionState&);
-    void addCommittedTransactions(
-            int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
-            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
-            bool displayInfoChanged);
+    void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
+                                  const frontend::DisplayInfos&, bool displayInfoChanged);
     status_t writeToFile(std::string filename = FILE_NAME);
     void setBufferSize(size_t bufferSizeInBytes);
     void onLayerRemoved(int layerId);
@@ -88,8 +85,7 @@
     nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
     std::unordered_map<int, proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
     std::map<uint32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
-    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mStartingDisplayInfos
-            GUARDED_BY(mTraceLock);
+    frontend::DisplayInfos mStartingDisplayInfos GUARDED_BY(mTraceLock);
 
     std::set<uint32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
     TransactionProtoParser mProtoParser;
@@ -106,7 +102,7 @@
         std::vector<LayerCreationArgs> createdLayers;
         std::vector<uint32_t> destroyedLayerHandles;
         bool displayInfoChanged;
-        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
+        frontend::DisplayInfos displayInfos;
         int64_t vsyncId;
         int64_t timestamp;
     };
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 55004c5..72a11c6 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -53,7 +53,7 @@
     frontend::LayerLifecycleManager lifecycleManager;
     frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
     frontend::LayerSnapshotBuilder snapshotBuilder;
-    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
+    ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
 
     renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
     char value[PROPERTY_VALUE_MAX];
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 4d03be0..8e208bc 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -788,7 +788,7 @@
     }
 
 private:
-    void setVsyncEnabled(PhysicalDisplayId, bool) override {}
+    void requestHardwareVsync(PhysicalDisplayId, bool) override {}
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 40a5d57..18bd3b9 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -289,7 +289,7 @@
             IPCThreadState::self()->joinThreadPool();
             [&]() { exit(0); }();
         }
-        sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+        sp<IBinder> binder = defaultServiceManager()->waitForService(serviceName);
         remote = interface_cast<IIPCTest>(binder);
         remote->setDeathToken(mDeathRecipient);
     }
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
index 7077523..f1bb231 100644
--- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -85,7 +85,7 @@
     ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90inactive) {
     auto displayToken = mOuterDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
     mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -95,7 +95,7 @@
     ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothInnerActive) {
     auto displayToken = mInnerDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
     mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -110,7 +110,7 @@
     ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothOuterActive) {
     mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
     auto displayToken = mInnerDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index db81bad..86af303 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -105,12 +105,12 @@
         "SurfaceFlinger_DisplayModeSwitching.cpp",
         "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
         "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
+        "SurfaceFlinger_FoldableTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
         "SurfaceFlinger_GetDisplayStatsTest.cpp",
         "SurfaceFlinger_HdrOutputControlTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
         "SurfaceFlinger_InitializeDisplaysTest.cpp",
-        "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
         "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_PowerHintTest.cpp",
         "SurfaceFlinger_SetDisplayStateTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index e64cb38..ee12276 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -371,10 +371,11 @@
     // Called by tests to inject a HWC display setup
     template <bool kInitPowerMode = true>
     static void injectHwcDisplay(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
-                                Return(Error::NONE)));
         if constexpr (kInitPowerMode) {
+            EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+                    .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+                                    Return(Error::NONE)));
+
             EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
                     .WillOnce(Return(Error::NONE));
         }
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d26ef3c..8911430 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -1198,7 +1198,7 @@
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -1234,8 +1234,8 @@
     auto protoDroppedSurfaceFrameActualStart =
             createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
                                                displayFrameToken1, sPidOne, sLayerNameOne,
-                                               FrameTimelineEvent::PRESENT_DROPPED, false, false,
-                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PRESENT_DROPPED, true, false,
+                                               FrameTimelineEvent::JANK_DROPPED,
                                                FrameTimelineEvent::PREDICTION_VALID, true);
     auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
 
@@ -1470,7 +1470,7 @@
             createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_DROPPED, false, false,
-                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::JANK_DROPPED,
                                                FrameTimelineEvent::PREDICTION_EXPIRED, true);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index b8a7446..12cf070 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -105,7 +105,7 @@
 
     LayerHierarchyBuilder mHierarchyBuilder{{}};
     LayerSnapshotBuilder mSnapshotBuilder;
-    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
+    DisplayInfos mFrontEndDisplayInfos;
     renderengine::ShadowSettings globalShadowSettings;
     static const std::vector<uint32_t> STARTING_ZORDER;
 };
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8f1b450..91875cc 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -137,7 +137,7 @@
                 generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(),
                                                                         kEndTime.ns(),
                                                                         kPresentTime.ns())))
-            .WillOnce(Return(vsyncId.value));
+            .WillOnce(Return(ftl::to_underlying(vsyncId)));
     EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1);
     EXPECT_NO_FATAL_FAILURE(
             mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns()));
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 965e378..682c998 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -155,6 +155,33 @@
     EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
+TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
+    // Hardware VSYNC should not change if the display is already registered.
+    EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId1, false)).Times(0);
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
+
+    // TODO(b/241285191): Restore once VsyncSchedule::getPendingHardwareVsyncState is called by
+    // Scheduler::setDisplayPowerMode rather than SF::setPowerModeInternal.
+#if 0
+    // Hardware VSYNC should be disabled for newly registered displays.
+    EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId2, false)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId3, false)).Times(1);
+#endif
+
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+    mScheduler->registerDisplay(kDisplayId3,
+                                std::make_shared<RefreshRateSelector>(kDisplay3Modes,
+                                                                      kDisplay3Mode60->getId()));
+
+    EXPECT_FALSE(mScheduler->getVsyncSchedule(kDisplayId1)->getPendingHardwareVsyncState());
+    EXPECT_FALSE(mScheduler->getVsyncSchedule(kDisplayId2)->getPendingHardwareVsyncState());
+    EXPECT_FALSE(mScheduler->getVsyncSchedule(kDisplayId3)->getPendingHardwareVsyncState());
+}
+
 TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
     // The layer is registered at creation time and deregistered at destruction time.
     sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
new file mode 100644
index 0000000..bd2344c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct FoldableTest : DisplayTransactionTest {
+    static constexpr bool kWithMockScheduler = false;
+    FoldableTest() : DisplayTransactionTest(kWithMockScheduler) {}
+
+    void SetUp() override {
+        injectMockScheduler(kInnerDisplayId);
+
+        // Inject inner and outer displays with uninitialized power modes.
+        constexpr bool kInitPowerMode = false;
+        {
+            InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+            mInnerDisplay = injector.inject();
+        }
+        {
+            OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            mOuterDisplay = injector.inject();
+        }
+    }
+
+    static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
+    static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+
+    sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
+};
+
+TEST_F(FoldableTest, foldUnfold) {
+    // When the device boots, the inner display should be the pacesetter.
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+    // ...and should still be after powering on.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+    // The outer display should become the pacesetter after folding.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+    // The inner display should become the pacesetter after unfolding.
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+    // The inner display should stay the pacesetter if both are powered on.
+    // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+    // The outer display should become the pacesetter if designated.
+    mFlinger.scheduler()->setPacesetterDisplay(kOuterDisplayId);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+}
+
+TEST_F(FoldableTest, doesNotRequestHardwareVsyncIfPoweredOff) {
+    // Both displays are powered off.
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kInnerDisplayId, _))
+            .Times(0);
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kOuterDisplayId, _))
+            .Times(0);
+
+    EXPECT_FALSE(mInnerDisplay->isPoweredOn());
+    EXPECT_FALSE(mOuterDisplay->isPoweredOn());
+
+    auto& scheduler = *mFlinger.scheduler();
+    scheduler.onHardwareVsyncRequest(kInnerDisplayId, true);
+    scheduler.onHardwareVsyncRequest(kOuterDisplayId, true);
+}
+
+TEST_F(FoldableTest, requestsHardwareVsyncForInnerDisplay) {
+    // Only inner display is powered on.
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kInnerDisplayId, true))
+            .Times(1);
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kOuterDisplayId, _))
+            .Times(0);
+
+    // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
+    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+
+    EXPECT_TRUE(mInnerDisplay->isPoweredOn());
+    EXPECT_FALSE(mOuterDisplay->isPoweredOn());
+
+    auto& scheduler = *mFlinger.scheduler();
+    scheduler.onHardwareVsyncRequest(kInnerDisplayId, true);
+    scheduler.onHardwareVsyncRequest(kOuterDisplayId, true);
+}
+
+TEST_F(FoldableTest, requestsHardwareVsyncForOuterDisplay) {
+    // Only outer display is powered on.
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kInnerDisplayId, _))
+            .Times(0);
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kOuterDisplayId, true))
+            .Times(1);
+
+    // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
+    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+    EXPECT_FALSE(mInnerDisplay->isPoweredOn());
+    EXPECT_TRUE(mOuterDisplay->isPoweredOn());
+
+    auto& scheduler = *mFlinger.scheduler();
+    scheduler.onHardwareVsyncRequest(kInnerDisplayId, true);
+    scheduler.onHardwareVsyncRequest(kOuterDisplayId, true);
+}
+
+TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) {
+    // Both displays are powered on.
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kInnerDisplayId, true))
+            .Times(1);
+    EXPECT_CALL(mFlinger.mockSchedulerCallback(), requestHardwareVsync(kOuterDisplayId, true))
+            .Times(1);
+
+    // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
+    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+    EXPECT_TRUE(mInnerDisplay->isPoweredOn());
+    EXPECT_TRUE(mOuterDisplay->isPoweredOn());
+
+    auto& scheduler = *mFlinger.scheduler();
+    scheduler.onHardwareVsyncRequest(mInnerDisplay->getPhysicalId(), true);
+    scheduler.onHardwareVsyncRequest(mOuterDisplay->getPhysicalId(), true);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
deleted file mode 100644
index e38f56e..0000000
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include "DisplayTransactionTestHelpers.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace {
-
-struct MultiDisplayPacesetterTest : DisplayTransactionTest {
-    static constexpr bool kWithMockScheduler = false;
-    MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {}
-};
-
-TEST_F(MultiDisplayPacesetterTest, foldable) {
-    injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
-
-    // Inject inner and outer displays with uninitialized power modes.
-    sp<DisplayDevice> innerDisplay, outerDisplay;
-    constexpr bool kInitPowerMode = false;
-    {
-        InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
-        auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
-        injector.setPowerMode(std::nullopt);
-        injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
-        innerDisplay = injector.inject();
-    }
-    {
-        OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
-        auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
-        injector.setPowerMode(std::nullopt);
-        outerDisplay = injector.inject();
-    }
-
-    // When the device boots, the inner display should be the pacesetter.
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
-
-    // ...and should still be after powering on.
-    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
-
-    // The outer display should become the pacesetter after folding.
-    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
-
-    // The inner display should become the pacesetter after unfolding.
-    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
-
-    // The inner display should stay the pacesetter if both are powered on.
-    // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
-    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
-
-    // The outer display should become the pacesetter if designated.
-    mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId());
-    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 7754c21..cf3fab3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -61,7 +61,7 @@
 struct EventThreadBaseSupportedVariant {
     static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) {
         // Expect no change to hardware nor synthetic VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, _)).Times(0);
+        EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, _)).Times(0);
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 };
@@ -79,13 +79,13 @@
 struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
     static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         // Expect to enable hardware VSYNC and disable synthetic VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, true)).Times(1);
+        EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
     }
 
     static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         // Expect to disable hardware VSYNC and enable synthetic VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, false)).Times(1);
+        EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3b6a987..a30f7e0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -27,6 +27,7 @@
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncController.h"
+#include "Scheduler/VsyncSchedule.h"
 #include "mock/MockVSyncDispatch.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
@@ -80,9 +81,13 @@
                                                    new VsyncSchedule(displayId, std::move(tracker),
                                                                      std::make_shared<
                                                                              mock::VSyncDispatch>(),
-                                                                     std::move(controller))));
+                                                                     std::move(controller),
+                                                                     mockRequestHardwareVsync
+                                                                             .AsStdFunction())));
     }
 
+    testing::MockFunction<void(PhysicalDisplayId, bool)> mockRequestHardwareVsync;
+
     void unregisterDisplay(PhysicalDisplayId displayId) {
         ftl::FakeGuard guard(kMainThreadContext);
         Scheduler::unregisterDisplay(displayId);
@@ -163,6 +168,8 @@
                                           : VsyncSchedule::HwVsyncState::Disabled;
     }
 
+    using Scheduler::onHardwareVsyncRequest;
+
 private:
     // ICompositor overrides:
     void configure() override {}
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index dd72174..a95a645 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -122,7 +122,7 @@
     google::protobuf::RepeatedPtrField<proto::DisplayInfo> displayProtos;
     auto displayInfoProto = displayProtos.Add();
     *displayInfoProto = TransactionProtoParser::toProto(d1, layerStack);
-    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
+    frontend::DisplayInfos displayInfos;
     TransactionProtoParser::fromProto(displayProtos, displayInfos);
 
     ASSERT_TRUE(displayInfos.contains(ui::LayerStack::fromValue(layerStack)));
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
index 4010fa6..a8a3cd0 100644
--- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -25,7 +25,6 @@
 #include <scheduler/Fps.h>
 #include "Scheduler/VsyncSchedule.h"
 #include "ThreadContext.h"
-#include "mock/MockSchedulerCallback.h"
 #include "mock/MockVSyncDispatch.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
@@ -34,20 +33,21 @@
 
 namespace android {
 
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
 
 class VsyncScheduleTest : public testing::Test {
 protected:
     VsyncScheduleTest();
     ~VsyncScheduleTest() override;
 
-    scheduler::mock::SchedulerCallback mCallback;
+    testing::MockFunction<void(PhysicalDisplayId, bool)> mRequestHardwareVsync;
+
     const std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule =
             std::unique_ptr<scheduler::VsyncSchedule>(
-                    new scheduler::VsyncSchedule(DEFAULT_DISPLAY_ID,
-                                                 std::make_shared<mock::VSyncTracker>(),
+                    new scheduler::VsyncSchedule(kDisplayId, std::make_shared<mock::VSyncTracker>(),
                                                  std::make_shared<mock::VSyncDispatch>(),
-                                                 std::make_unique<mock::VsyncController>()));
+                                                 std::make_unique<mock::VsyncController>(),
+                                                 mRequestHardwareVsync.AsStdFunction()));
 
     mock::VsyncController& getController() {
         return *static_cast<mock::VsyncController*>(&mVsyncSchedule->getController());
@@ -75,21 +75,21 @@
 }
 
 TEST_F(VsyncScheduleTest, EnableDoesNothingWhenDisallowed) {
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
 
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 }
 
 TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed) {
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
 
-    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(false /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) {
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
 
-    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(true /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, MakeAllowed) {
@@ -98,33 +98,33 @@
 
 TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
 
-    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(false /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
 
-    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(true /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
 
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 }
 
 TEST_F(VsyncScheduleTest, EnableWorksOnce) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
 
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
+    mVsyncSchedule->enableHardwareVsync();
 }
 
 TEST_F(VsyncScheduleTest, AllowedIsSticky) {
@@ -134,22 +134,22 @@
 
 TEST_F(VsyncScheduleTest, EnableDisable) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
 
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
-    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, false));
+    mVsyncSchedule->disableHardwareVsync(false /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, EnableDisable2) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
 
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
-    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, false));
+    mVsyncSchedule->disableHardwareVsync(true /* disallow */);
 }
 
 TEST_F(VsyncScheduleTest, StartPeriodTransition) {
@@ -159,22 +159,22 @@
 
     const Period period = (60_Hz).getPeriod();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
     EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
 
-    mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+    mVsyncSchedule->startPeriodTransition(period, false);
 }
 
 TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
     const Period period = (60_Hz).getPeriod();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
     EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
 
-    mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+    mVsyncSchedule->startPeriodTransition(period, false);
 }
 
 TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) {
@@ -182,20 +182,20 @@
 
     const Period period = (60_Hz).getPeriod();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
     EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true));
 
-    mVsyncSchedule->startPeriodTransition(mCallback, period, true);
+    mVsyncSchedule->startPeriodTransition(period, true);
 }
 
 TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) {
     const Period period = (60_Hz).getPeriod();
     const auto timestamp = TimePoint::now();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
     EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
 
-    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+    mVsyncSchedule->addResyncSample(timestamp, period);
 }
 
 TEST_F(VsyncScheduleTest, AddResyncSampleDisabled) {
@@ -203,40 +203,40 @@
     const Period period = (60_Hz).getPeriod();
     const auto timestamp = TimePoint::now();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
     EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
 
-    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+    mVsyncSchedule->addResyncSample(timestamp, period);
 }
 
 TEST_F(VsyncScheduleTest, AddResyncSampleReturnsTrue) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
     const Period period = (60_Hz).getPeriod();
     const auto timestamp = TimePoint::now();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
     EXPECT_CALL(getController(),
                 addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
             .WillOnce(Return(true));
 
-    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+    mVsyncSchedule->addResyncSample(timestamp, period);
 }
 
 TEST_F(VsyncScheduleTest, AddResyncSampleReturnsFalse) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    mVsyncSchedule->enableHardwareVsync(mCallback);
+    mVsyncSchedule->enableHardwareVsync();
 
     const Period period = (60_Hz).getPeriod();
     const auto timestamp = TimePoint::now();
 
-    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+    EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, false));
     EXPECT_CALL(getController(),
                 addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
             .WillOnce(Return(false));
 
-    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+    mVsyncSchedule->addResyncSample(timestamp, period);
 }
 
 TEST_F(VsyncScheduleTest, PendingState) FTL_FAKE_GUARD(kMainThreadContext) {
@@ -250,19 +250,19 @@
 
 TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) {
     ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
-    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(false /* disallow */);
     ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
 }
 
 TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(true /* disallow */);
     ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
 }
 
 TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
-    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    mVsyncSchedule->disableHardwareVsync(false /* disallow */);
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
 }
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index a8eca21..306eb4d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -23,14 +23,14 @@
 namespace android::scheduler::mock {
 
 struct SchedulerCallback final : ISchedulerCallback {
-    MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, bool), (override));
+    MOCK_METHOD(void, requestHardwareVsync, (PhysicalDisplayId, bool), (override));
     MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
-    void setVsyncEnabled(PhysicalDisplayId, bool) override {}
+    void requestHardwareVsync(PhysicalDisplayId, bool) override {}
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index aaeb8f9..4c0910a 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -102,18 +102,6 @@
     ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
 }
 
-TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 5; i++) {
-        threads.push_back(std::thread(
-                [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    ASSERT_TRUE(waitForCallbacks(5, 25ms));
-    ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
-}
-
 TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
     mScheduler->schedule(createCallback(1), 5ms);
     mScheduler.reset(nullptr);