Merge "Update thermal headroom listener API doc for NDK" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 07d16f7..a4a22a0 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -22,5 +22,21 @@
     {
       "name": "SurfaceFlinger_test"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "SurfaceFlinger_test",
+      "keywords": [ "primary-device" ],
+      "options": [
+	// TODO(b/328119950) Known to be broken.
+        {
+          "exclude-filter": "LayerCallbackTest#SetNullBuffer"
+        },
+	// TODO(b/398306512) Flaky on real device.
+        {
+          "exclude-filter": "LayerRenderTypeTransactionTests/LayerRenderTypeTransactionTest#SetRelativeZBasic_BufferQueue/*"
+        }
+      ]
+    }
   ]
 }
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index fdac5db..316f04c 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -316,97 +316,6 @@
     write /sys/kernel/debug/tracing/tracing_on 0
     write /sys/kernel/tracing/tracing_on 0
 
-# Only create the tracing instance if persist.mm_events.enabled
-# Attempting to remove the tracing instance after it has been created
-# will likely fail with EBUSY as it would be in use by traced_probes.
-on mm_events_property_available && property:persist.mm_events.enabled=true
-# Create MM Events Tracing Instance for Kmem Activity Trigger
-    mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system
-    mkdir /sys/kernel/tracing/instances/mm_events 0755 system system
-
-# Read and set per CPU buffer size
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb
-
-# Set the default buffer size to the minimum
-    write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1
-    write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1
-
-# Read and enable tracing
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on
-
-# Tracing disabled by default
-    write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0
-    write /sys/kernel/tracing/instances/mm_events/tracing_on 0
-
-# Read and truncate kernel trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/trace
-
-# Enable trace events
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable
-
-# Read and clear per-CPU raw kernel trace
-# Cannot use wildcards in .rc files. Update this if there is a phone with
-# more CPUs.
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu0/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu0/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu1/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu1/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu2/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu2/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu3/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu3/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu4/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu4/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu5/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu5/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu6/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu6/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu7/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu7/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu8/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu8/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu9/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu9/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu10/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu10/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu11/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu11/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu12/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu12/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu13/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu13/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu14/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace
-    chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
-    chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
-
-on property:ro.persistent_properties.ready=true
-    trigger mm_events_property_available
-
 # Handle hyp tracing instance
 on late-init && property:ro.boot.hypervisor.vm.supported=1
 
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a5d176d..fdb032b 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -117,6 +117,7 @@
         "libdumpsys",
         "libserviceutils",
         "android.tracing.flags_c_lib",
+        "perfetto_flags_c_lib",
     ],
 }
 
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 888fb67..9e3e2b0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -57,6 +57,7 @@
 #include <log/log_read.h>
 #include <math.h>
 #include <openssl/sha.h>
+#include <perfetto_flags.h>
 #include <poll.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -190,7 +191,7 @@
 #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
 #define LINKERCONFIG_DIR "/linkerconfig"
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
-#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
+#define SYSTEM_TRACE_DIR "/data/misc/perfetto-traces/bugreport"
 #define CGROUPFS_DIR "/sys/fs/cgroup"
 #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
 #define DROPBOX_DIR "/data/system/dropbox"
@@ -359,6 +360,31 @@
     return CopyFileToFd(input_file, out_fd.get());
 }
 
+template <typename Func>
+size_t ForEachTrace(Func func) {
+    std::unique_ptr<DIR, decltype(&closedir)> traces_dir(opendir(SYSTEM_TRACE_DIR), closedir);
+
+    if (traces_dir == nullptr) {
+        MYLOGW("Unable to open directory %s: %s\n", SYSTEM_TRACE_DIR, strerror(errno));
+        return 0;
+    }
+
+    size_t traces_found = 0;
+    struct dirent* entry = nullptr;
+    while ((entry = readdir(traces_dir.get()))) {
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+        std::string trace_path = std::string(SYSTEM_TRACE_DIR) + "/" + entry->d_name;
+        if (access(trace_path.c_str(), F_OK) != 0) {
+            continue;
+        }
+        ++traces_found;
+        func(trace_path);
+    }
+    return traces_found;
+}
+
 }  // namespace
 }  // namespace os
 }  // namespace android
@@ -1101,20 +1127,16 @@
     // This function copies into the .zip the system trace that was snapshotted
     // by the early call to MaybeSnapshotSystemTraceAsync(), if any background
     // tracing was happening.
-    bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
-    if (!system_trace_exists) {
-        // No background trace was happening at the time MaybeSnapshotSystemTraceAsync() was invoked
-        if (!PropertiesHelper::IsUserBuild()) {
-            MYLOGI(
-                "No system traces found. Check for previously uploaded traces by looking for "
-                "go/trace-uuid in logcat")
-        }
-        return;
+    size_t traces_found = android::os::ForEachTrace([&](const std::string& trace_path) {
+        ds.AddZipEntry(ZIP_ROOT_DIR + trace_path, trace_path);
+        android::os::UnlinkAndLogOnError(trace_path);
+    });
+
+    if (traces_found == 0 && !PropertiesHelper::IsUserBuild()) {
+        MYLOGI(
+            "No system traces found. Check for previously uploaded traces by looking for "
+            "go/trace-uuid in logcat")
     }
-    ds.AddZipEntry(
-            ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT,
-            SYSTEM_TRACE_SNAPSHOT);
-    android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT);
 }
 
 static void DumpVisibleWindowViews() {
@@ -3412,8 +3434,8 @@
     // duration is logged into MYLOG instead.
     PrintHeader();
 
-    bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
-    if (options_->use_predumped_ui_data && !system_trace_exists) {
+    size_t trace_count = android::os::ForEachTrace([](const std::string&) {});
+    if (options_->use_predumped_ui_data && trace_count == 0) {
         MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
         options_->use_predumped_ui_data = false;
     }
@@ -3560,20 +3582,24 @@
     }
 
     // If a stale file exists already, remove it.
-    unlink(SYSTEM_TRACE_SNAPSHOT);
+    android::os::ForEachTrace([&](const std::string& trace_path) { unlink(trace_path.c_str()); });
 
     MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str())
+
     return std::async(
         std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] {
-            // If a background system trace is happening and is marked as "suitable for
-            // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
-            // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
-            // case that no trace is ongoing, this command is a no-op.
+            // If one or more background system traces are happening and are marked as
+            // "suitable for bugreport" (bugreport_score > 0 in the trace config), this command
+            // will snapshot them into SYSTEM_TRACE_DIR.
+            // In the (likely) case that no trace is ongoing, this command is a no-op.
             // Note: this should not be enqueued as we need to freeze the trace before
             // dumpstate starts. Otherwise the trace ring buffers will contain mostly
             // the dumpstate's own activity which is irrelevant.
+            const char* cmd_arg = perfetto::flags::save_all_traces_in_bugreport()
+                                      ? "--save-all-for-bugreport"
+                                      : "--save-for-bugreport";
             RunCommand(
-                SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"},
+                SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", cmd_arg},
                 CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
                 false, outFd);
             // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
index 0aff200..7e3307d 100644
--- a/cmds/dumpstate/dumpstate_smoke_test.xml
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -22,7 +22,9 @@
         <option name="cleanup" value="true" />
         <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
     </target_preparer>
-
+    <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
+        <option name="flag-value" value="perfetto/perfetto.flags.save_all_traces_in_bugreport=true" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="dumpstate_smoke_test" />
diff --git a/cmds/dumpstate/res/default_screenshot.png b/cmds/dumpstate/res/default_screenshot.png
index 10f36aa..1e14306 100644
--- a/cmds/dumpstate/res/default_screenshot.png
+++ b/cmds/dumpstate/res/default_screenshot.png
Binary files differ
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index a29923a..c72847c 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -24,8 +24,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <libgen.h>
+#include <signal.h>
 #include <ziparchive/zip_archive.h>
 
+#include <cstdio>
 #include <fstream>
 #include <regex>
 
@@ -603,6 +605,93 @@
         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
 }
 
+class DumpstateTracingTest : public Test {
+  protected:
+    void TearDown() override {
+        for (int pid : bg_process_pids) {
+            kill(pid, SIGKILL);
+        }
+    }
+
+    void StartTracing(const std::string& config) {
+        // Write the perfetto config into a file.
+        const int id = static_cast<int>(bg_process_pids.size());
+        char cfg[64];
+        snprintf(cfg, sizeof(cfg), "/data/misc/perfetto-configs/br-%d", id);
+        unlink(cfg);  // Remove the config file if it exists already.
+        FILE* f = fopen(cfg, "w");
+        ASSERT_NE(f, nullptr);
+        fputs(config.c_str(), f);
+        fclose(f);
+
+        // Invoke perfetto to start tracing.
+        char cmd[255];
+        snprintf(cmd, sizeof(cmd), "perfetto --background-wait --txt -o /dev/null -c %s", cfg);
+        FILE* proc = popen(cmd, "r");
+        ASSERT_NE(proc, nullptr);
+
+        // Read back the PID of the background process. We will use it to kill
+        // all tracing sessions when the test ends or fails.
+        char pid_str[32]{};
+        ASSERT_NE(fgets(pid_str, sizeof(pid_str), proc), nullptr);
+        int pid = atoi(pid_str);
+        bg_process_pids.push_back(pid);
+
+        pclose(proc);
+        unlink(cfg);
+    }
+
+    std::vector<int> bg_process_pids;
+};
+
+TEST_F(DumpstateTracingTest, ManyTracesInBugreport) {
+    // Note the trace duration is irrelevant and is only an upper bound.
+    // Tracing is stopped as soon as the bugreport.zip creation ends.
+    StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+  config {
+    name: "linux.ftrace"
+  }
+}
+
+duration_ms: 120000
+bugreport_filename: "sys.pftrace"
+bugreport_score: 100
+)");
+
+    StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+  config {
+    name: "linux.ftrace"
+  }
+}
+
+duration_ms: 120000
+bugreport_score: 50
+bugreport_filename: "mem.pftrace"
+)");
+
+    ZippedBugreportGenerationTest::GenerateBugreport();
+    std::string zip_path = ZippedBugreportGenerationTest::getZipFilePath();
+    ZipArchiveHandle handle;
+    ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
+
+    const char* kExpectedEntries[]{
+        "FS/data/misc/perfetto-traces/bugreport/sys.pftrace",
+        "FS/data/misc/perfetto-traces/bugreport/mem.pftrace",
+    };
+
+    // Check that the bugreport contains both traces.
+    for (const char* file_path : kExpectedEntries) {
+        ZipEntry entry{};
+        GetEntry(handle, file_path, &entry);
+        EXPECT_GT(entry.uncompressed_length, 100);
+    }
+    CloseArchive(handle);
+}
+
 }  // namespace dumpstate
 }  // namespace os
 }  // namespace android
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 77e7328..a2e171f 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -202,26 +202,13 @@
 }
 
 bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
-        sp<GLConsumer>* glConsumer, EGLSurface* surface) {
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-    sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true);
+                                         sp<GLConsumer>* glConsumer, EGLSurface* surface) {
+    auto [glc, surf] = GLConsumer::create(name, GL_TEXTURE_EXTERNAL_OES, false, true);
     glc->setDefaultBufferSize(w, h);
-    glc->getSurface()->setMaxDequeuedBufferCount(2);
     glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+    surf->setMaxDequeuedBufferCount(2);
+    sp<ANativeWindow> anw = surf;
 
-    sp<ANativeWindow> anw = glc->getSurface();
-#else
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-    sp<GLConsumer> glc = new GLConsumer(consumer, name,
-            GL_TEXTURE_EXTERNAL_OES, false, true);
-    glc->setDefaultBufferSize(w, h);
-    producer->setMaxDequeuedBufferCount(2);
-    glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
-
-    sp<ANativeWindow> anw = new Surface(producer);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr);
     if (s == EGL_NO_SURFACE) {
         fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
@@ -254,7 +241,7 @@
     status_t err;
 
     if (mSurfaceComposerClient == nullptr) {
-        mSurfaceComposerClient = new SurfaceComposerClient;
+        mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
     }
     err = mSurfaceComposerClient->initCheck();
     if (err != NO_ERROR) {
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 36dcbca..b87ef2d 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -25,13 +25,8 @@
     name: "idlcli-defaults",
     shared_libs: [
         "android.hardware.vibrator-V3-ndk",
-        "android.hardware.vibrator@1.0",
-        "android.hardware.vibrator@1.1",
-        "android.hardware.vibrator@1.2",
-        "android.hardware.vibrator@1.3",
         "libbase",
         "libbinder_ndk",
-        "libhidlbase",
         "liblog",
         "libutils",
     ],
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index 262f2e5..dc52c57 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -18,7 +18,6 @@
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
 
 #include <android/binder_enums.h>
-#include <hidl/HidlSupport.h>
 
 #include <iomanip>
 #include <iostream>
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index b943495..1a9993e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -22,102 +22,30 @@
 #include <aidl/android/hardware/vibrator/IVibratorManager.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
 
 #include "IdlCli.h"
 #include "utils.h"
 
 namespace android {
 
-using hardware::Return;
+using ::aidl::android::hardware::vibrator::IVibrator;
 using idlcli::IdlCli;
 
-static constexpr int NUM_TRIES = 2;
-
-// Creates a Return<R> with STATUS::EX_NULL_POINTER.
-template <class R>
-inline R NullptrStatus() {
-    using ::android::hardware::Status;
-    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
-}
-
-template <>
-inline ndk::ScopedAStatus NullptrStatus() {
-    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
-}
-
-template <typename I>
 inline auto getService(std::string name) {
-    const auto instance = std::string() + I::descriptor + "/" + name;
+    const auto instance = std::string() + IVibrator::descriptor + "/" + name;
     auto vibBinder = ndk::SpAIBinder(AServiceManager_checkService(instance.c_str()));
-    return I::fromBinder(vibBinder);
+    return IVibrator::fromBinder(vibBinder);
 }
 
-template <>
-inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) {
-    return android::hardware::vibrator::V1_0::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) {
-    return android::hardware::vibrator::V1_1::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) {
-    return android::hardware::vibrator::V1_2::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) {
-    return android::hardware::vibrator::V1_3::IVibrator::getService(name);
-}
-
-template <typename I>
-using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>;
-
-template <typename I>
-class HalWrapper {
-public:
-    static std::unique_ptr<HalWrapper> Create() {
-        // Assume that if getService returns a nullptr, HAL is not available on the
-        // device.
-        const auto name = IdlCli::Get().getName();
-        auto hal = getService<I>(name.empty() ? "default" : name);
-        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
-    }
-
-    template <class R, class... Args0, class... Args1>
-    R call(R (I::*fn)(Args0...), Args1&&... args1) {
-        return (*mHal.*fn)(std::forward<Args1>(args1)...);
-    }
-
-private:
-    HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
-
-private:
-    shared_ptr<I> mHal;
-};
-
-template <typename I>
 static auto getHal() {
-    static auto sHalWrapper = HalWrapper<I>::Create();
-    return sHalWrapper.get();
-}
-
-template <class R, class I, class... Args0, class... Args1>
-R halCall(R (I::*fn)(Args0...), Args1&&... args1) {
-    auto hal = getHal<I>();
-    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
+    // Assume that if getService returns a nullptr, HAL is not available on the device.
+    const auto name = IdlCli::Get().getName();
+    return getService(name.empty() ? "default" : name);
 }
 
 namespace idlcli {
 namespace vibrator {
 
-namespace V1_0 = ::android::hardware::vibrator::V1_0;
-namespace V1_1 = ::android::hardware::vibrator::V1_1;
-namespace V1_2 = ::android::hardware::vibrator::V1_2;
-namespace V1_3 = ::android::hardware::vibrator::V1_3;
 namespace aidl = ::aidl::android::hardware::vibrator;
 
 class VibratorCallback : public aidl::BnVibratorCallback {
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
index 9afa300..cae6909 100644
--- a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -51,21 +51,17 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
-
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        auto status = hal->alwaysOnDisable(mId);
 
-        return ret;
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
     int32_t mId;
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
index bb7f9f2..410ca52 100644
--- a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -72,21 +72,17 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
-
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        auto status = hal->alwaysOnEnable(mId, mEffect, mStrength);
 
-        return ret;
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
     int32_t mId;
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index eb9008b..41acb98 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -89,7 +89,7 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto hal = getHal<aidl::IVibrator>();
+        auto hal = getHal();
 
         if (!hal) {
             return UNAVAILABLE;
@@ -104,7 +104,7 @@
             callback = ndk::SharedRefBase::make<VibratorCallback>();
         }
 
-        auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+        auto status = hal->compose(mComposite, callback);
 
         if (status.isOk() && callback) {
             callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp
index b8308ce..5f6bf86 100644
--- a/cmds/idlcli/vibrator/CommandComposePwle.cpp
+++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp
@@ -163,7 +163,7 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto hal = getHal<aidl::IVibrator>();
+        auto hal = getHal();
 
         if (!hal) {
             return UNAVAILABLE;
@@ -178,7 +178,7 @@
             callback = ndk::SharedRefBase::make<VibratorCallback>();
         }
 
-        auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback);
+        auto status = hal->composePwle(mCompositePwle, callback);
 
         if (status.isOk() && callback) {
             callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
index 6d3cf84..bd682ea 100644
--- a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
+++ b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
@@ -108,7 +108,7 @@
     }
 
     Status doMain(Args&& /*args*/) override {
-        auto hal = getHal<aidl::IVibrator>();
+        auto hal = getHal();
 
         if (!hal) {
             return UNAVAILABLE;
@@ -123,7 +123,7 @@
             callback = ndk::SharedRefBase::make<VibratorCallback>();
         }
 
-        auto status = hal->call(&aidl::IVibrator::composePwleV2, mCompositePwle, callback);
+        auto status = hal->composePwleV2(mCompositePwle, callback);
 
         if (status.isOk() && callback) {
             callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
index aa01a11..44115e9 100644
--- a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
+++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
@@ -44,29 +44,38 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        std::vector<float> bandwidthAmplitude;
-        float frequencyMinimumHz;
-        float frequencyResolutionHz;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status =
-                hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude);
-            statusStr = status.getDescription();
-            ret = (status.isOk() ? OK : ERROR);
-
-            status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
-            ret = (status.isOk() ? OK : ERROR);
-
-            status =
-                hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
-            ret = (status.isOk() ? OK : ERROR);
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<float> bandwidthAmplitude;
+        float frequencyMinimumHz;
+        float frequencyResolutionHz;
+
+        auto status = hal->getBandwidthAmplitudeMap(&bandwidthAmplitude);
+
+        if (!status.isOk()) {
+            std::cout << "Status: " << status.getDescription() << std::endl;
+            return ERROR;
+        }
+
+        status = hal->getFrequencyMinimum(&frequencyMinimumHz);
+
+        if (!status.isOk()) {
+            std::cout << "Status: " << status.getDescription() << std::endl;
+            return ERROR;
+        }
+
+        status = hal->getFrequencyResolution(&frequencyResolutionHz);
+
+        if (!status.isOk()) {
+            std::cout << "Status: " << status.getDescription() << std::endl;
+            return ERROR;
+        }
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Bandwidth Amplitude Map: " << std::endl;
         float frequency = frequencyMinimumHz;
         for (auto &e : bandwidthAmplitude) {
@@ -74,7 +83,7 @@
             frequency += frequencyResolutionHz;
         }
 
-        return ret;
+        return OK;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 303a989..507d871 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -42,22 +42,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t cap;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t cap;
+        auto status = hal->getCapabilities(&cap);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index 10508bd..1c1eb3c 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t maxDelayMs;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxDelayMs;
+        auto status = hal->getCompositionDelayMax(&maxDelayMs);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 900cb18..cfd4c53 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t maxSize;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxSize;
+        auto status = hal->getCompositionSizeMax(&maxSize);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Max Size: " << maxSize << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
index 504c648..2a61446 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        float frequencyMinimumHz;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        float frequencyMinimumHz;
+        auto status = hal->getFrequencyMinimum(&frequencyMinimumHz);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
index de35838..157d6bf 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
@@ -44,23 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        float frequencyResolutionHz;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status =
-                hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        float frequencyResolutionHz;
+        auto status = hal->getFrequencyResolution(&frequencyResolutionHz);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
index 2edd0ca..2eb4510 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
@@ -46,26 +46,22 @@
     }
 
     Status doMain(Args&& /*args*/) override {
-        std::string statusStr;
-        std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getFrequencyToOutputAccelerationMap,
-                                    &frequencyToOutputAccelerationMap);
-            statusStr = status.getDescription();
-            ret = (status.isOk() ? OK : ERROR);
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
+        auto status = hal->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Frequency to Output Amplitude Map: " << std::endl;
         for (auto& entry : frequencyToOutputAccelerationMap) {
             std::cout << entry.frequencyHz << " " << entry.maxOutputAccelerationGs << std::endl;
         }
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
index 460d39e..c957f6b 100644
--- a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -57,22 +57,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t duration;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t duration;
+        auto status = hal->getPrimitiveDuration(mPrimitive, &duration);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Duration: " << duration << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 
     CompositePrimitive mPrimitive;
diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
index b2c3551..c1b0278 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t maxSize;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxSize;
+        auto status = hal->getPwleCompositionSizeMax(&maxSize);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Max Size: " << maxSize << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
index 9081973..ed00ba0 100644
--- a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        int32_t maxDurationMs;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxDurationMs;
+        auto status = hal->getPwlePrimitiveDurationMax(&maxDurationMs);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
index cca072c..f780b8b 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args&& /*args*/) override {
-        std::string statusStr;
-        int32_t maxSize;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPwleV2CompositionSizeMax, &maxSize);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxSize;
+        auto status = hal->getPwleV2CompositionSizeMax(&maxSize);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Max Size: " << maxSize << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
index dbbfe1a..e84e969 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
@@ -44,23 +44,19 @@
     }
 
     Status doMain(Args&& /*args*/) override {
-        std::string statusStr;
-        int32_t maxDurationMs;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMaxMillis,
-                                    &maxDurationMs);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t maxDurationMs;
+        auto status = hal->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
index 09225c4..448fd2a 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
@@ -44,23 +44,19 @@
     }
 
     Status doMain(Args&& /*args*/) override {
-        std::string statusStr;
-        int32_t minDurationMs;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMinMillis,
-                                    &minDurationMs);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        int32_t minDurationMs;
+        auto status = hal->getPwleV2PrimitiveDurationMinMillis(&minDurationMs);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Primitive duration min: " << minDurationMs << " ms" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetQFactor.cpp b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
index a2681e9..e04bad9 100644
--- a/cmds/idlcli/vibrator/CommandGetQFactor.cpp
+++ b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
@@ -42,22 +42,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        float qFactor;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getQFactor, &qFactor);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        float qFactor;
+        auto status = hal->getQFactor(&qFactor);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Q Factor: " << qFactor << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
index 81a6391..e222ea6 100644
--- a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
+++ b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
@@ -44,22 +44,19 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        float resonantFrequencyHz;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getResonantFrequency, &resonantFrequencyHz);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        float resonantFrequencyHz;
+        auto status = hal->getResonantFrequency(&resonantFrequencyHz);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Resonant Frequency: " << resonantFrequencyHz << " Hz" << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
index edfcd91..9b05540 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -44,25 +44,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        std::vector<Effect> effects;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<Effect> effects;
+        auto status = hal->getSupportedAlwaysOnEffects(&effects);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Effects:" << std::endl;
         for (auto &e : effects) {
             std::cout << "  " << toString(e) << std::endl;
         }
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
index b326e07..f95f682 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
@@ -44,25 +44,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        std::vector<Braking> braking;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<Braking> braking;
+        auto status = hal->getSupportedBraking(&braking);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Braking Mechanisms:" << std::endl;
         for (auto &e : braking) {
             std::cout << "  " << toString(e) << std::endl;
         }
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
index 7658f22..05de1b8 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -44,25 +44,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        std::vector<Effect> effects;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<Effect> effects;
+        auto status = hal->getSupportedEffects(&effects);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Effects:" << std::endl;
         for (auto &e : effects) {
             std::cout << "  " << toString(e) << std::endl;
         }
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
index d101681..0f33f0f 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -44,25 +44,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        std::vector<CompositePrimitive> primitives;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::vector<CompositePrimitive> primitives;
+        auto status = hal->getSupportedPrimitives(&primitives);
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Primitives:" << std::endl;
         for (auto &e : primitives) {
             std::cout << "  " << toString(e) << std::endl;
         }
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index cedb9fe..e55b44a 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -42,24 +42,17 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::off);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else if (auto hal = getHal<V1_0::IVibrator>()) {
-            auto status = hal->call(&V1_0::IVibrator::off);
-            statusStr = toString(status);
-            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        auto status = hal->off();
 
-        return ret;
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 8212fc1..856c219 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -67,34 +67,27 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
-        std::shared_ptr<VibratorCallback> callback;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            ABinderProcess_setThreadPoolMaxThreadCount(1);
-            ABinderProcess_startThreadPool();
-
-            int32_t cap;
-            hal->call(&aidl::IVibrator::getCapabilities, &cap);
-
-            if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
-                callback = ndk::SharedRefBase::make<VibratorCallback>();
-            }
-
-            auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
-
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else if (auto hal = getHal<V1_0::IVibrator>()) {
-            auto status = hal->call(&V1_0::IVibrator::on, mDuration);
-            statusStr = toString(status);
-            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        if (ret == OK && mBlocking) {
+        std::shared_ptr<VibratorCallback> callback;
+
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
+
+        int32_t cap;
+        hal->getCapabilities(&cap);
+
+        if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        auto status = hal->on(mDuration, callback);
+
+        if (status.isOk() && mBlocking) {
             if (callback) {
                 callback->waitForComplete();
             } else {
@@ -102,9 +95,9 @@
             }
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Status: " << status.getDescription() << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 
     bool mBlocking;
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index c897686..0a354e2 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -28,34 +28,6 @@
 
 namespace vibrator {
 
-/*
- * The following static asserts are only relevant here because the argument
- * parser uses a single implementation for determining the string names.
- */
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
-              static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
-              static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
-              static_cast<uint8_t>(aidl::EffectStrength::STRONG));
-static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
-              static_cast<uint8_t>(aidl::Effect::CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
-              static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD));
-static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP));
-static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
-              static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
-              static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
-              static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
-              static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
-              static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
-
 using aidl::Effect;
 using aidl::EffectStrength;
 
@@ -107,61 +79,31 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        uint32_t lengthMs;
-        Status ret;
-        std::shared_ptr<VibratorCallback> callback;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            ABinderProcess_setThreadPoolMaxThreadCount(1);
-            ABinderProcess_startThreadPool();
-
-            int32_t cap;
-            hal->call(&aidl::IVibrator::getCapabilities, &cap);
-
-            if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
-                callback = ndk::SharedRefBase::make<VibratorCallback>();
-            }
-
-            int32_t aidlLengthMs;
-            auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
-                                    &aidlLengthMs);
-
-            statusStr = status.getDescription();
-            lengthMs = static_cast<uint32_t>(aidlLengthMs);
-            ret = status.isOk() ? OK : ERROR;
-        } else {
-            Return<void> hidlRet;
-            V1_0::Status status;
-            auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
-                status = retStatus;
-                lengthMs = retLengthMs;
-            };
-
-            if (auto hal = getHal<V1_3::IVibrator>()) {
-                hidlRet =
-                        hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
-                                  static_cast<V1_0::EffectStrength>(mStrength), callback);
-            } else if (auto hal = getHal<V1_2::IVibrator>()) {
-                hidlRet =
-                        hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
-                                  static_cast<V1_0::EffectStrength>(mStrength), callback);
-            } else if (auto hal = getHal<V1_1::IVibrator>()) {
-                hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
-                                    static_cast<V1_1::Effect_1_1>(mEffect),
-                                    static_cast<V1_0::EffectStrength>(mStrength), callback);
-            } else if (auto hal = getHal<V1_0::IVibrator>()) {
-                hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
-                                    static_cast<V1_0::EffectStrength>(mStrength), callback);
-            } else {
-                return UNAVAILABLE;
-            }
-
-            statusStr = toString(status);
-            ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+        if (!hal) {
+            return UNAVAILABLE;
         }
 
-        if (ret == OK && mBlocking) {
+        uint32_t lengthMs;
+        std::shared_ptr<VibratorCallback> callback;
+
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
+
+        int32_t cap;
+        hal->getCapabilities(&cap);
+
+        if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        int32_t aidlLengthMs;
+        auto status = hal->perform(mEffect, mStrength, callback, &aidlLengthMs);
+
+        lengthMs = static_cast<uint32_t>(aidlLengthMs);
+
+        if (status.isOk() && mBlocking) {
             if (callback) {
                 callback->waitForComplete();
             } else {
@@ -169,10 +111,10 @@
             }
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Status: " << status.getDescription() << std::endl;
         std::cout << "Length: " << lengthMs << std::endl;
 
-        return ret;
+        return status.isOk() ? OK : ERROR;
     }
 
     bool mBlocking;
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 8b8058c..8050723 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -50,25 +50,17 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::setAmplitude,
-                                    static_cast<float>(mAmplitude) / UINT8_MAX);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else if (auto hal = getHal<V1_0::IVibrator>()) {
-            auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
-            statusStr = toString(status);
-            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        auto status = hal->setAmplitude(static_cast<float>(mAmplitude) / UINT8_MAX);
 
-        return ret;
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
     uint8_t mAmplitude;
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 1795793..8f8d4b7 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -48,24 +48,17 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
+        auto hal = getHal();
 
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else if (auto hal = getHal<V1_3::IVibrator>()) {
-            auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
-            statusStr = toString(status);
-            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
-        } else {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        auto status = hal->setExternalControl(mEnable);
 
-        return ret;
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
     bool mEnable;
diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
index cdc529a..31ee954 100644
--- a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
@@ -42,15 +42,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl);
+        auto hal = getHal();
 
-        if (!ret.isOk()) {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+        int32_t cap;
 
-        return OK;
+        auto status = hal->getCapabilities(&cap);
+
+        bool hasAmplitudeControl = cap & IVibrator::CAP_AMPLITUDE_CONTROL;
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+        std::cout << "Result: " << std::boolalpha << hasAmplitudeControl << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
index ed15d76..f0c542c 100644
--- a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
@@ -42,15 +42,22 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_3::IVibrator::supportsExternalControl);
+        auto hal = getHal();
 
-        if (!ret.isOk()) {
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+        int32_t cap;
 
-        return OK;
+        auto status = hal->getCapabilities(&cap);
+
+        bool hasExternalControl = cap & IVibrator::CAP_EXTERNAL_CONTROL;
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+        std::cout << "Result: " << std::boolalpha << hasExternalControl << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 };
 
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index fc4cfc9..d53c94b 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -18,10 +18,6 @@
     {
       "name": "run_dex2oat_test"
     },
-    // AdoptableHostTest moves packages, part of which is handled by installd
-    {
-      "name": "AdoptableHostTest"
-    },
     {
       "name": "CtsUsesLibraryHostTestCases"
     },
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index cd8ca89..f320504 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -119,12 +119,24 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.nfc.ese.prebuilt.xml",
+    src: "android.hardware.nfc.ese.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.nfc.hce.prebuilt.xml",
     src: "android.hardware.nfc.hce.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
 prebuilt_etc {
+    name: "android.hardware.nfc.hcef.prebuilt.xml",
+    src: "android.hardware.nfc.hcef.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.reboot_escrow.prebuilt.xml",
     src: "android.hardware.reboot_escrow.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -317,6 +329,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.telephony.messaging.prebuilt.xml",
+    src: "android.hardware.telephony.messaging.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.thread_network.prebuilt.xml",
     src: "android.hardware.thread_network.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -505,6 +523,12 @@
 }
 
 prebuilt_etc {
+    name: "com.nxp.mifare.prebuilt.xml",
+    src: "com.nxp.mifare.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "go_handheld_core_hardware.prebuilt.xml",
     src: "go_handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.messaging.xml b/data/etc/android.hardware.telephony.messaging.xml
new file mode 100644
index 0000000..0e96123
--- /dev/null
+++ b/data/etc/android.hardware.telephony.messaging.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 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.
+-->
+
+<!-- Feature for devices with messaging. -->
+<permissions>
+  <feature name="android.hardware.telephony" />
+  <feature name="android.hardware.telephony.radio.access" />
+  <feature name="android.hardware.telephony.subscription" />
+  <feature name="android.hardware.telephony.messaging" />
+</permissions>
diff --git a/include/OWNERS b/include/OWNERS
index c98e87a..7f847e8 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -1,5 +1,4 @@
 alecmouri@google.com
-alexeykuzmin@google.com
 dangittik@google.com
 jreck@google.com
 lajos@google.com
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 46c7dfe..a291db0 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -26,6 +26,7 @@
 #ifndef ANDROID_CONFIGURATION_H
 #define ANDROID_CONFIGURATION_H
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #include <android/asset_manager.h>
diff --git a/include/android/input.h b/include/android/input.h
index 5f44550..2f6c5b5 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -862,7 +862,7 @@
     AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
     AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5,
     AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6,
-    // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs)
+    // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs,/frameworks/native/services/inputflinger/tests/fuzzers/FuzzedInputStream.h)
 };
 
 /**
diff --git a/include/android/system_health.h b/include/android/system_health.h
index bdb1413..4494e32 100644
--- a/include/android/system_health.h
+++ b/include/android/system_health.h
@@ -316,8 +316,6 @@
 /**
  * Gets the range of the calculation window size for CPU headroom.
  *
- * In API version 36, the range will be a superset of [50, 10000].
- *
  * Available since API level 36.
  *
  * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
@@ -332,8 +330,6 @@
 /**
  * Gets the range of the calculation window size for GPU headroom.
  *
- * In API version 36, the range will be a superset of [50, 10000].
- *
  * Available since API level 36.
  *
  * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
diff --git a/include/ftl/ignore.h b/include/ftl/ignore.h
new file mode 100644
index 0000000..1468fa2
--- /dev/null
+++ b/include/ftl/ignore.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2025 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
+
+namespace android::ftl {
+
+// An alternative to `std::ignore` that makes it easy to ignore multiple values.
+//
+// Examples:
+//
+//   void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) {
+//     // When invoked, all the arguments are ignored.
+//     ftl::ignore(arg1, arg2, arg3);
+//   }
+//
+//   void ftl_ignore_single(int arg) {
+//     // It can be used like std::ignore to ignore a single value
+//     ftl::ignore = arg;
+//   }
+//
+inline constexpr struct {
+  // NOLINTNEXTLINE(misc-unconventional-assign-operator, readability-named-parameter)
+  constexpr auto operator=(auto&&) const -> decltype(*this) { return *this; }
+  // NOLINTNEXTLINE(readability-named-parameter)
+  constexpr void operator()(auto&&...) const {}
+} ignore;
+
+}  // namespace android::ftl
\ No newline at end of file
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h
index 3ae865a..9fc080d 100644
--- a/include/input/DisplayTopologyGraph.h
+++ b/include/input/DisplayTopologyGraph.h
@@ -46,6 +46,8 @@
     DisplayTopologyPosition position;
     // The offset in DP of the adjacent display, relative to the source display.
     float offsetDp;
+
+    std::string dump() const;
 };
 
 /**
@@ -55,6 +57,9 @@
     ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID;
     std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph;
     std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
+
+    bool isValid() const;
+    std::string dump() const;
 };
 
 } // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index e84023e..002b3a7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -316,6 +316,19 @@
 
 bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties);
 
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+                        int32_t action);
+
+bool isFromMouse(uint32_t source, ToolType tooltype);
+
+bool isFromTouchpad(uint32_t source, ToolType tooltype);
+
+bool isFromDrawingTablet(uint32_t source, ToolType tooltype);
+
+bool isHoverAction(int32_t action);
+
+bool isMouseOrTouchpad(uint32_t sources);
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
diff --git a/include/input/InputFlags.h b/include/input/InputFlags.h
new file mode 100644
index 0000000..16e754e
--- /dev/null
+++ b/include/input/InputFlags.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2025 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
+
+namespace android {
+
+class InputFlags {
+public:
+    /**
+     * Check if connected displays feature is enabled, either via the feature flag or settings
+     * override. Developer setting override allows enabling all the "desktop experiences" features
+     * including input related connected_displays_cursor flag.
+     *
+     * The developer settings override is prioritised over aconfig flags. Any tests that require
+     * applicable aconfig flags to be disabled with SCOPED_FLAG_OVERRIDE also need this developer
+     * option to be reset locally.
+     *
+     * Also note the developer setting override is only applicable to the desktop experiences
+     * related features.
+     *
+     * To enable only the input flag run:
+     *      adb shell aflags enable com.android.input.flags.connected_displays_cursor
+     * To override this flag and enable all "desktop experiences" features run:
+     *      adb shell aflags enable com.android.window.flags.enable_desktop_mode_through_dev_option
+     *      adb shell setprop persist.wm.debug.desktop_experience_devopts 1
+     */
+    static bool connectedDisplaysCursorEnabled();
+
+    /**
+     * Check if both connectedDisplaysCursor and associatedDisplayCursorBugfix is enabled.
+     */
+    static bool connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled();
+};
+
+} // namespace android
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index f4f4d5e..e22481f 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -73,6 +73,9 @@
             int tgid, int uid) override;
     virtual HalResult<void> closeSessionChannel(int tgid, int uid) override;
     virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
+    virtual HalResult<void> sendCompositionData(
+            const std::vector<hal::CompositionData>& data) override;
+    virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override;
 
 private:
     std::mutex mConnectedHalMutex;
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 4290182..17a4cd4 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -18,6 +18,8 @@
 
 #include <aidl/android/hardware/power/Boost.h>
 #include <aidl/android/hardware/power/ChannelConfig.h>
+#include <aidl/android/hardware/power/CompositionData.h>
+#include <aidl/android/hardware/power/CompositionUpdate.h>
 #include <aidl/android/hardware/power/IPower.h>
 #include <aidl/android/hardware/power/IPowerHintSession.h>
 #include <aidl/android/hardware/power/Mode.h>
@@ -37,6 +39,8 @@
 
 namespace power {
 
+namespace hal = aidl::android::hardware::power;
+
 // State of Power HAL support for individual apis.
 enum class HalSupport {
     UNKNOWN = 0,
@@ -49,21 +53,20 @@
 public:
     virtual ~HalWrapper() = default;
 
-    virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
-                                     int32_t durationMs) = 0;
-    virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
+    virtual HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) = 0;
+    virtual HalResult<void> setMode(hal::Mode mode, bool enabled) = 0;
     virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) = 0;
     virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
-            aidl::android::hardware::power::SessionTag tag,
-            aidl::android::hardware::power::SessionConfig* config) = 0;
+            hal::SessionTag tag, hal::SessionConfig* config) = 0;
     virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
-    virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
-                                                                                       int uid) = 0;
+    virtual HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) = 0;
     virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0;
     virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() = 0;
+    virtual HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) = 0;
+    virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) = 0;
 };
 
 // Empty Power HAL wrapper that ignores all api calls.
@@ -72,21 +75,20 @@
     EmptyHalWrapper() = default;
     ~EmptyHalWrapper() override = default;
 
-    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
-                             int32_t durationMs) override;
-    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override;
+    HalResult<void> setMode(hal::Mode mode, bool enabled) override;
     HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) override;
     HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
-            aidl::android::hardware::power::SessionTag tag,
-            aidl::android::hardware::power::SessionConfig* config) override;
+            hal::SessionTag tag, hal::SessionConfig* config) override;
     HalResult<int64_t> getHintSessionPreferredRate() override;
-    HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
-                                                                               int uid) override;
+    HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override;
     HalResult<void> closeSessionChannel(int tgid, int uid) override;
     HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
+    HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override;
+    HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override;
 
 protected:
     virtual const char* getUnsupportedMessage();
@@ -99,9 +101,8 @@
           : mHandleV1_0(std::move(handleV1_0)) {}
     ~HidlHalWrapperV1_0() override = default;
 
-    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
-                             int32_t durationMs) override;
-    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override;
+    HalResult<void> setMode(hal::Mode mode, bool enabled) override;
 
 protected:
     const sp<hardware::power::V1_0::IPower> mHandleV1_0;
@@ -127,9 +128,8 @@
 // Wrapper for the HIDL Power HAL v1.2.
 class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
 public:
-    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
-                             int32_t durationMs) override;
-    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override;
+    HalResult<void> setMode(hal::Mode mode, bool enabled) override;
     explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
           : HidlHalWrapperV1_1(std::move(handleV1_2)) {}
     ~HidlHalWrapperV1_2() override = default;
@@ -141,7 +141,7 @@
 // Wrapper for the HIDL Power HAL v1.3.
 class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
 public:
-    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<void> setMode(hal::Mode mode, bool enabled) override;
     explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
           : HidlHalWrapperV1_2(std::move(handleV1_3)) {}
     ~HidlHalWrapperV1_3() override = default;
@@ -153,26 +153,24 @@
 // Wrapper for the AIDL Power HAL.
 class AidlHalWrapper : public EmptyHalWrapper {
 public:
-    explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle)
-          : mHandle(std::move(handle)) {}
+    explicit AidlHalWrapper(std::shared_ptr<hal::IPower> handle) : mHandle(std::move(handle)) {}
     ~AidlHalWrapper() override = default;
 
-    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
-                             int32_t durationMs) override;
-    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override;
+    HalResult<void> setMode(hal::Mode mode, bool enabled) override;
     HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) override;
     HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
-            aidl::android::hardware::power::SessionTag tag,
-            aidl::android::hardware::power::SessionConfig* config) override;
+            hal::SessionTag tag, hal::SessionConfig* config) override;
 
     HalResult<int64_t> getHintSessionPreferredRate() override;
-    HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
-                                                                               int uid) override;
+    HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override;
     HalResult<void> closeSessionChannel(int tgid, int uid) override;
     HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
+    HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override;
+    HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override;
 
 protected:
     const char* getUnsupportedMessage() override;
@@ -181,16 +179,10 @@
     // Control access to the boost and mode supported arrays.
     std::mutex mBoostMutex;
     std::mutex mModeMutex;
-    std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
-    std::array<HalSupport,
-               static_cast<int32_t>(
-                       *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) +
-                       1>
+    std::shared_ptr<hal::IPower> mHandle;
+    std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Boost>().end() - 1)) + 1>
             mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
-    std::array<HalSupport,
-               static_cast<int32_t>(
-                       *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
-                       1>
+    std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Mode>().end() - 1)) + 1>
             mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
 };
 
diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h
index ba6fe77..0134e02 100644
--- a/include/powermanager/PowerHintSessionWrapper.h
+++ b/include/powermanager/PowerHintSessionWrapper.h
@@ -45,9 +45,11 @@
     virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type,
                                     bool in_enabled);
     virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig();
+    std::optional<int> getSessionId();
 
 private:
     std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession;
+    std::optional<int> mSessionId;
     int32_t mInterfaceVersion;
 };
 
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
index 4dbb0ea..76811f2 100644
--- a/libs/attestation/OWNERS
+++ b/libs/attestation/OWNERS
@@ -1,2 +1 @@
-chaviw@google.com
-svv@google.com
\ No newline at end of file
+svv@google.com
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index fb00d4f..a3499c1 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -277,15 +277,6 @@
         "-fvisibility=hidden",
         "-DBUILDING_LIBBINDER",
     ],
-
-    target: {
-        vendor: {
-            // Trimming the exported symbols reveals a bug in vendor code, so
-            // disable it for the vendor variant for now. http://b/349657329
-            // TODO: Fix the issue and remove this override.
-            cflags: ["-fvisibility=default"],
-        },
-    },
 }
 
 cc_defaults {
@@ -827,6 +818,7 @@
     // so we restrict its visibility to the Trusty-specific packages.
     visibility: [
         ":__subpackages__",
+        "//hardware/interfaces/security/see:__subpackages__",
         "//system/core/trusty:__subpackages__",
         "//vendor:__subpackages__",
     ],
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 7c0319a..b1c8994 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -130,7 +130,13 @@
 
 bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
     sp<ProcessState> self = ProcessState::selfOrNull();
-    if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
+    // Should not cache if process state could not be found, or if thread pool
+    // max could is not greater than zero.
+    if (!self) {
+        ALOGW("Service retrieved before binder threads started. If they are to be started, "
+              "consider starting binder threads earlier.");
+        return false;
+    } else if (self->getThreadPoolMaxTotalThreadCount() <= 0) {
         ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
               "implemented. serviceName: %s",
               serviceName.c_str());
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index bc7ae37..9883eb2 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -445,6 +445,9 @@
     const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/,
     uint32_t /*flags*/)
 {
+    // BBinder::linkToDeath is invalid because this process owns this binder.
+    // The DeathRecipient is called on BpBinders when the process owning the
+    // binder node dies.
     return INVALID_OPERATION;
 }
 
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 16023ff..1f3a45a 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -188,7 +188,9 @@
 }
 
 status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
-    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
+    return setupClient([&, fd = std::move(fd),
+                        request = std::move(request)](const std::vector<uint8_t>& sessionId,
+                                                      bool incoming) mutable -> status_t {
         if (!fd.ok()) {
             fd = request();
             if (!fd.ok()) return BAD_VALUE;
@@ -476,8 +478,10 @@
     return server;
 }
 
-status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
-                                                              bool incoming)>& connectAndInit) {
+template <typename Fn,
+          typename /* = std::enable_if_t<std::is_invocable_r_v<
+                      status_t, Fn, const std::vector<uint8_t>&, bool>> */>
+status_t RpcSession::setupClient(Fn&& connectAndInit) {
     {
         RpcMutexLockGuard _l(mMutex);
         LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once");
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 3819fb6..14c0bde 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -21,6 +21,7 @@
 #include <log/log.h>
 #include <poll.h>
 #include <trusty/tipc.h>
+#include <type_traits>
 
 #include "FdTrigger.h"
 #include "RpcState.h"
@@ -32,6 +33,9 @@
 
 namespace android {
 
+// Corresponds to IPC_MAX_MSG_HANDLES in the Trusty kernel
+constexpr size_t kMaxTipcHandles = 8;
+
 // RpcTransport for writing Trusty IPC clients in Android.
 class RpcTransportTipcAndroid : public RpcTransport {
 public:
@@ -78,12 +82,28 @@
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
             const std::optional<SmallFunction<status_t()>>& altPoll,
             const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
+        bool sentFds = false;
         auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
-            // TODO: send ancillaryFds. For now, we just abort if anyone tries
-            // to send any.
-            LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(),
-                                "File descriptors are not supported on Trusty yet");
-            return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0));
+            trusty_shm shms[kMaxTipcHandles] = {{0}};
+            ssize_t shm_count = 0;
+
+            if (!sentFds && ancillaryFds != nullptr && !ancillaryFds->empty()) {
+                if (ancillaryFds->size() > kMaxTipcHandles) {
+                    ALOGE("Too many file descriptors for TIPC: %zu", ancillaryFds->size());
+                    errno = EINVAL;
+                    return -1;
+                }
+                for (const auto& fdVariant : *ancillaryFds) {
+                    shms[shm_count++] = {std::visit([](const auto& fd) { return fd.get(); },
+                                                    fdVariant),
+                                         TRUSTY_SEND_SECURE_OR_SHARE};
+                }
+            }
+
+            auto ret = TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs,
+                                                    (shm_count == 0) ? nullptr : shms, shm_count));
+            sentFds |= ret >= 0;
+            return ret;
         };
 
         status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn,
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS
index 3100518..2617a16 100644
--- a/libs/binder/aidl/android/content/pm/OWNERS
+++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -1,5 +1,4 @@
+michaelwr@google.com
 narayan@google.com
 patb@google.com
-svetoslavganov@google.com
-toddke@google.com
-patb@google.com
\ No newline at end of file
+schfan@google.com
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index af37bf2..c9f92da 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -25,6 +25,7 @@
 
 #include <map>
 #include <optional>
+#include <type_traits>
 #include <vector>
 
 namespace android {
@@ -291,9 +292,14 @@
     // join on thread passed to preJoinThreadOwnership
     static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
 
-    [[nodiscard]] status_t setupClient(
-            const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
-                    connectAndInit);
+    // This is a workaround to support move-only functors.
+    // TODO: use std::move_only_function when it becomes available.
+    template <typename Fn,
+              // Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning
+              // status_t
+              typename = std::enable_if_t<
+                      std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>>
+    [[nodiscard]] status_t setupClient(Fn&& connectAndInit);
     [[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
     [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
                                                     const std::vector<uint8_t>& sessionId,
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index bcbd14f..e848385 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -79,7 +79,7 @@
     template <typename T>
     typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
             const Parcel& parcel, sp<T>* t) const {
-        *t = new T{};
+        *t = sp<T>::make();
         return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
     }
     template <typename T>
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 48c0ea6..1949cbb 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -134,9 +134,14 @@
 //
 // param will be passed to requestFd. Callers can use param to pass contexts to
 // the requestFd function.
+//
+// paramDeleteFd will be called when requestFd and param are no longer in use.
+// Callers can pass a function deleting param if necessary. Callers can set
+// paramDeleteFd to null if callers doesn't need to clean up param.
 AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
                                               int (*requestFd)(void* param),
-                                              void* param);
+                                              void* param,
+                                              void (*paramDeleteFd)(void* param));
 
 // Sets the file descriptor transport mode for this session.
 void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 64b1be2..8177fb0 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -248,9 +248,18 @@
 #endif // __TRUSTY__
 
 AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
-                                              void* param) {
+                                              void* param, void (*paramDeleteFd)(void* param)) {
     auto session = handleToStrongPointer<RpcSession>(handle);
-    auto request = [=] { return unique_fd{requestFd(param)}; };
+    auto deleter = [=](void* param) {
+        if (paramDeleteFd) {
+            paramDeleteFd(param);
+        }
+    };
+    // TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function.
+    std::shared_ptr<void> sharedParam(param, deleter);
+    auto request = [=, sharedParam = std::move(sharedParam)] {
+        return unique_fd{requestFd(sharedParam.get())};
+    };
     if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
         ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str());
         return nullptr;
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 89f21dd..783e11f 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -62,6 +62,8 @@
  * This must be called before the object is sent to another process.
  * Aborts on invalid values. Not thread safe.
  *
+ * This overrides the setting in ABinderProcess_disableBackgroundScheduling.
+ *
  * \param binder local server binder to set the policy for
  * \param policy scheduler policy as defined in linux UAPI
  * \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 6aff994..2432099 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -75,6 +75,19 @@
 void ABinderProcess_joinThreadPool(void);
 
 /**
+ * Disables (or enables) background scheduling.
+ *
+ * By default, binder threads execute at a lower priority. However, this can cause
+ * priority inversion, so it is recommended to be disabled in high priority
+ * or in system processes.
+ *
+ * See also AIBinder_setMinSchedulerPolicy, which overrides this setting.
+ *
+ * \param disable whether to disable background scheduling
+ */
+void ABinderProcess_disableBackgroundScheduling(bool disable);
+
+/**
  * This gives you an fd to wait on. Whenever data is available on the fd,
  * ABinderProcess_handlePolledCommands can be called to handle binder queries.
  * This is expected to be used in a single threaded process which waits on
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 089c775..8050205 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -27,6 +27,10 @@
 
 #if defined(__ANDROID_VENDOR__)
 
+#if defined(__ANDROID_PRODUCT__)
+#error "build bug: product is not part of the vendor half of the Treble system/vendor split"
+#endif
+
 /**
  * Private addition to binder_flag_t.
  */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index a637165..d4eb8c7 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -229,6 +229,7 @@
         AIBinder_fromPlatformBinder*;
         AIBinder_toPlatformBinder*;
         AParcel_viewPlatformParcel*;
+        ABinderProcess_disableBackgroundScheduling;
     };
   local:
     *;
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0072ac3..bcdb959 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -36,6 +36,10 @@
     IPCThreadState::self()->joinThreadPool();
 }
 
+void ABinderProcess_disableBackgroundScheduling(bool disable) {
+    IPCThreadState::disableBackgroundScheduling(disable);
+}
+
 binder_status_t ABinderProcess_setupPolling(int* fd) {
     return IPCThreadState::self()->setupPolling(fd);
 }
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 7e5c9dd..774bb21 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -20,6 +20,8 @@
 mod session;
 
 pub use server::RpcServer;
+#[cfg(target_os = "trusty")]
+pub use server::RpcServerConnection;
 #[cfg(not(target_os = "trusty"))]
 pub use server::RpcServerRef;
 pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs
index fe45dec..54d82d5 100644
--- a/libs/binder/rust/rpcbinder/src/server/trusty.rs
+++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs
@@ -106,6 +106,10 @@
     ctx: *mut c_void,
 }
 
+// SAFETY: The opaque handle: `ctx` points into a dynamic allocation,
+// and not tied to anything specific to the current thread.
+unsafe impl Send for RpcServerConnection {}
+
 impl Drop for RpcServerConnection {
     fn drop(&mut self) {
         // We do not need to close handle_fd since we do not own it.
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 09688a2..411b9de 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -195,11 +195,13 @@
     /// take ownership of) file descriptors already connected to it.
     pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
         &self,
-        mut request_fd: impl FnMut() -> Option<RawFd>,
+        request_fd: impl FnMut() -> Option<RawFd>,
     ) -> Result<Strong<T>, StatusCode> {
-        // Double reference the factory because trait objects aren't FFI safe.
-        let mut request_fd_ref: RequestFd = &mut request_fd;
-        let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+        // Trait objects aren't FFI safe, so *mut c_void can't be converted back to
+        // *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get
+        // the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks.
+        let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd);
+        let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void;
 
         // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
         // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
@@ -209,6 +211,7 @@
                 self.as_ptr(),
                 Some(request_fd_wrapper),
                 param,
+                Some(param_delete_fd_wrapper),
             ))
         };
         Self::get_interface(service)
@@ -225,13 +228,18 @@
     }
 }
 
-type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
-
 unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
-    let request_fd_ptr = param as *mut RequestFd;
+    let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>;
     // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
     // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
     // initialized instance.
     let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
     request_fd().unwrap_or(-1)
 }
+
+unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) {
+    // SAFETY: This is only ever called by RpcPreconnectedClient, with param being the
+    // pointer returned from Box::into_raw.
+    let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) };
+    drop(request_fd_box);
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 8c0501b..771c65b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1160,6 +1160,12 @@
             pub const fn enum_values() -> [Self; $size] {
                 [$(Self::$name),*]
             }
+
+            #[inline(always)]
+            #[allow(missing_docs)]
+            pub const fn get(&self) -> $backing {
+                self.0
+            }
         }
 
         impl std::fmt::Debug for $enum {
@@ -1201,5 +1207,45 @@
                 Ok(v.map(|v| v.into_iter().map(Self).collect()))
             }
         }
+
+        impl std::ops::BitOr for $enum {
+            type Output = Self;
+            fn bitor(self, rhs: Self) -> Self {
+                Self(self.0 | rhs.0)
+            }
+        }
+
+        impl std::ops::BitOrAssign for $enum {
+            fn bitor_assign(&mut self, rhs: Self) {
+                self.0 = self.0 | rhs.0;
+            }
+        }
+
+        impl std::ops::BitAnd for $enum {
+            type Output = Self;
+            fn bitand(self, rhs: Self) -> Self {
+                Self(self.0 & rhs.0)
+            }
+        }
+
+        impl std::ops::BitAndAssign for $enum {
+            fn bitand_assign(&mut self, rhs: Self) {
+                self.0 = self.0 & rhs.0;
+            }
+        }
+
+        impl std::ops::BitXor for $enum {
+            type Output = Self;
+            fn bitxor(self, rhs: Self) -> Self {
+                Self(self.0 ^ rhs.0)
+            }
+        }
+
+        impl std::ops::BitXorAssign for $enum {
+            fn bitxor_assign(&mut self, rhs: Self) {
+                self.0 = self.0 ^ rhs.0;
+            }
+        }
+
     };
 }
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index c0cac83..609334e 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -28,8 +28,9 @@
     /// `num_threads` additional threads as specified by
     /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
     ///
-    /// This should be done before creating any Binder client or server. If
-    /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+    /// If this is called, it must be done before creating any Binder client or server.
+    ///
+    /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are
     /// called, then some things (such as callbacks and
     /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
     /// not work: the callbacks will be queued but never called as there is no
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 170b2ad..7c9c452 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -382,7 +382,7 @@
                 sockaddr_un addr_un{};
                 addr_un.sun_family = AF_UNIX;
                 strcpy(addr_un.sun_path, serverConfig.addr.c_str());
-                addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+                std::memcpy(&addr, &addr_un, sizeof(sockaddr_un));
                 addrLen = sizeof(sockaddr_un);
 
                 status = session->setupPreconnectedClient({}, [=]() {
@@ -394,7 +394,7 @@
                 sockaddr_un addr_un{};
                 addr_un.sun_family = AF_UNIX;
                 strcpy(addr_un.sun_path, serverConfig.addr.c_str());
-                addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+                std::memcpy(&addr, &addr_un, sizeof(sockaddr_un));
                 addrLen = sizeof(sockaddr_un);
 
                 status = session->setupUnixDomainClient(serverConfig.addr.c_str());
@@ -409,7 +409,7 @@
                         .svm_port = static_cast<unsigned int>(serverInfo.port),
                         .svm_cid = VMADDR_CID_LOCAL,
                 };
-                addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm);
+                std::memcpy(&addr, &addr_vm, sizeof(sockaddr_vm));
                 addrLen = sizeof(sockaddr_vm);
 
                 status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port);
@@ -420,7 +420,7 @@
                 addr_in.sin_family = AF_INET;
                 addr_in.sin_port = htons(serverInfo.port);
                 inet_aton(ip_addr.c_str(), &addr_in.sin_addr);
-                addr = *reinterpret_cast<sockaddr_storage*>(&addr_in);
+                std::memcpy(&addr, &addr_in, sizeof(sockaddr_in));
                 addrLen = sizeof(sockaddr_in);
 
                 status = session->setupInetClient(ip_addr.c_str(), serverInfo.port);
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index cac054e..457eaa5 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -109,6 +109,9 @@
         "libcutils",
         "libutils",
     ],
+    header_libs: [
+        "libaidl_transactions",
+    ],
     local_include_dirs: ["include_random_parcel"],
     export_include_dirs: ["include_random_parcel"],
 }
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 02e69cc..11aa768 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <aidl/transaction_ids.h>
 #include <fuzzbinder/libbinder_driver.h>
 
 #include <fuzzbinder/random_parcel.h>
@@ -31,6 +33,28 @@
     fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
 }
 
+uint32_t getCode(FuzzedDataProvider& provider) {
+    if (provider.ConsumeBool()) {
+        return provider.ConsumeIntegral<uint32_t>();
+    }
+
+    // Most of the AIDL services will have small set of transaction codes.
+    if (provider.ConsumeBool()) {
+        return provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+    }
+
+    if (provider.ConsumeBool()) {
+        return provider.PickValueInArray<uint32_t>(
+                {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+                 IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+                 IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+                 IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+    }
+
+    return provider.ConsumeIntegralInRange<uint32_t>(aidl::kLastMetaMethodId,
+                                                     aidl::kFirstMetaMethodId);
+}
+
 void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
     RandomParcelOptions options{
             .extraBinders = binders,
@@ -61,16 +85,7 @@
     }
 
     while (provider.remaining_bytes() > 0) {
-        // Most of the AIDL services will have small set of transaction codes.
-        // TODO(b/295942369) : Add remaining transact codes from IBinder.h
-        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
-                : provider.ConsumeBool()
-                ? provider.ConsumeIntegralInRange<uint32_t>(0, 100)
-                : provider.PickValueInArray<uint32_t>(
-                          {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
-                           IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
-                           IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
-                           IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+        uint32_t code = getCode(provider);
         uint32_t flags = provider.ConsumeIntegral<uint32_t>();
         Parcel data;
         // for increased fuzz coverage
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 583ad01..1ac00ca 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -94,9 +94,17 @@
     static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) {
         auto rpcServer = sp<RpcServer>::make(std::move(ctx));
 
-        // 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.
-        LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0));
+        // By default we use the latest stable version.
+        LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION));
+
+        // The default behavior in trusty is to allow handles to be passed with tipc IPC.
+        // We add mode NONE so that servers do not reject connections from clients who do
+        // not change their default transport mode.
+        static const std::vector<RpcSession::FileDescriptorTransportMode>
+                TRUSTY_SERVER_SUPPORTED_FD_MODES = {RpcSession::FileDescriptorTransportMode::TRUSTY,
+                                                    RpcSession::FileDescriptorTransportMode::NONE};
+
+        rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES);
 
         return rpcServer;
     }
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
index ef1b7c3..40fc218 100644
--- a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
@@ -30,6 +30,8 @@
 	trusty/user/base/lib/libstdc++-trusty \
 	trusty/user/base/lib/trusty-sys \
 
+MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp
+
 MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
 
 MODULE_BINDGEN_FLAGS += \
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index a9bd11e..43ee33e 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -101,7 +101,7 @@
         "android.hardware.automotive.remoteaccess.IRemoteAccess",
         "android.hardware.automotive.vehicle.IVehicle",
         "android.hardware.biometrics.face.IBiometricsFace",
-        "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
+        "android.hardware.biometrics.fingerprint.IFingerprint",
         "android.hardware.camera.provider.ICameraProvider",
         "android.hardware.drm.IDrmFactory",
         "android.hardware.graphics.allocator.IAllocator",
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 08ce855..5244442 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -26,6 +26,7 @@
         "function_test.cpp",
         "future_test.cpp",
         "hash_test.cpp",
+        "ignore_test.cpp",
         "match_test.cpp",
         "mixins_test.cpp",
         "non_null_test.cpp",
diff --git a/libs/ftl/ignore_test.cpp b/libs/ftl/ignore_test.cpp
new file mode 100644
index 0000000..5d5c67b
--- /dev/null
+++ b/libs/ftl/ignore_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2025 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 <string>
+
+#include <ftl/ignore.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+namespace {
+
+// Keep in sync with the example usage in the header file.
+
+void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) {
+  // When invoked, all the arguments are ignored.
+  ftl::ignore(arg1, arg2, arg3);
+}
+
+void ftl_ignore_single(int arg) {
+  // It can be used like std::ignore to ignore a single value
+  ftl::ignore = arg;
+}
+
+}  // namespace
+
+TEST(Ignore, Example) {
+  // The real example test is that there are no compiler warnings for unused arguments above.
+
+  // Use the example functions to avoid a compiler warning about unused functions.
+  ftl_ignore_multiple(0, "a", "b");
+  ftl_ignore_single(0);
+}
+
+}  // namespace android::test
diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp
index 6974da9..9e7a4cf 100644
--- a/libs/graphicsenv/FeatureOverrides.cpp
+++ b/libs/graphicsenv/FeatureOverrides.cpp
@@ -14,22 +14,179 @@
  * limitations under the License.
  */
 
-#include <graphicsenv/FeatureOverrides.h>
+#include <cinttypes>
 
 #include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <graphicsenv/FeatureOverrides.h>
 
 namespace android {
 
 using base::StringAppendF;
 
+status_t FeatureConfig::writeToParcel(Parcel* parcel) const {
+    status_t status;
+
+    status = parcel->writeUtf8AsUtf16(mFeatureName);
+    if (status != OK) {
+        return status;
+    }
+    status = parcel->writeBool(mEnabled);
+    if (status != OK) {
+        return status;
+    }
+    // Number of GPU vendor IDs.
+    status = parcel->writeVectorSize(mGpuVendorIDs);
+    if (status != OK) {
+        return status;
+    }
+    // GPU vendor IDs.
+    for (const auto& vendorID : mGpuVendorIDs) {
+        status = parcel->writeUint32(vendorID);
+        if (status != OK) {
+            return status;
+        }
+    }
+
+    return OK;
+}
+
+status_t FeatureConfig::readFromParcel(const Parcel* parcel) {
+    status_t status;
+
+    status = parcel->readUtf8FromUtf16(&mFeatureName);
+    if (status != OK) {
+        return status;
+    }
+    status = parcel->readBool(&mEnabled);
+    if (status != OK) {
+        return status;
+    }
+    // Number of GPU vendor IDs.
+    int numGpuVendorIDs;
+    status = parcel->readInt32(&numGpuVendorIDs);
+    if (status != OK) {
+        return status;
+    }
+    // GPU vendor IDs.
+    for (int i = 0; i < numGpuVendorIDs; i++) {
+        uint32_t gpuVendorIdUint;
+        status = parcel->readUint32(&gpuVendorIdUint);
+        if (status != OK) {
+            return status;
+        }
+        mGpuVendorIDs.emplace_back(gpuVendorIdUint);
+    }
+
+    return OK;
+}
+
 std::string FeatureConfig::toString() const {
     std::string result;
     StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str());
     StringAppendF(&result, "      Status: %s\n", mEnabled ? "enabled" : "disabled");
+    for (const auto& vendorID : mGpuVendorIDs) {
+        // vkjson outputs decimal, so print both formats.
+        StringAppendF(&result, "      GPU Vendor ID: 0x%04X (%d)\n", vendorID, vendorID);
+    }
 
     return result;
 }
 
+status_t FeatureOverrides::writeToParcel(Parcel* parcel) const {
+    status_t status;
+    // Number of global feature configs.
+    status = parcel->writeVectorSize(mGlobalFeatures);
+    if (status != OK) {
+        return status;
+    }
+    // Global feature configs.
+    for (const auto& cfg : mGlobalFeatures) {
+        status = cfg.writeToParcel(parcel);
+        if (status != OK) {
+            return status;
+        }
+    }
+    // Number of package feature overrides.
+    status = parcel->writeInt32(static_cast<int32_t>(mPackageFeatures.size()));
+    if (status != OK) {
+        return status;
+    }
+    for (const auto& feature : mPackageFeatures) {
+        // Package name.
+        status = parcel->writeUtf8AsUtf16(feature.first);
+        if (status != OK) {
+            return status;
+        }
+        // Number of package feature configs.
+        status = parcel->writeVectorSize(feature.second);
+        if (status != OK) {
+            return status;
+        }
+        // Package feature configs.
+        for (const auto& cfg : feature.second) {
+            status = cfg.writeToParcel(parcel);
+            if (status != OK) {
+                return status;
+            }
+        }
+    }
+
+    return OK;
+}
+
+status_t FeatureOverrides::readFromParcel(const Parcel* parcel) {
+    status_t status;
+
+    // Number of global feature configs.
+    status = parcel->resizeOutVector(&mGlobalFeatures);
+    if (status != OK) {
+        return status;
+    }
+    // Global feature configs.
+    for (FeatureConfig& cfg : mGlobalFeatures) {
+        status = cfg.readFromParcel(parcel);
+        if (status != OK) {
+            return status;
+        }
+    }
+
+    // Number of package feature overrides.
+    int numPkgOverrides;
+    status = parcel->readInt32(&numPkgOverrides);
+    if (status != OK) {
+        return status;
+    }
+    // Package feature overrides.
+    for (int i = 0; i < numPkgOverrides; i++) {
+        // Package name.
+        std::string name;
+        status = parcel->readUtf8FromUtf16(&name);
+        if (status != OK) {
+            return status;
+        }
+        std::vector<FeatureConfig> cfgs;
+        // Number of package feature configs.
+        int numCfgs;
+        status = parcel->readInt32(&numCfgs);
+        if (status != OK) {
+            return status;
+        }
+        // Package feature configs.
+        for (int j = 0; j < numCfgs; j++) {
+            FeatureConfig cfg;
+            status = cfg.readFromParcel(parcel);
+            if (status != OK) {
+                return status;
+            }
+            cfgs.emplace_back(cfg);
+        }
+        mPackageFeatures[name] = cfgs;
+    }
+
+    return OK;
+}
+
 std::string FeatureOverrides::toString() const {
     std::string result;
     result.append("Global Features:\n");
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4bc2611..03e6456 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -621,6 +621,10 @@
         mShouldUseAngle = true;
     }
     mShouldUseNativeDriver = shouldUseNativeDriver;
+
+    if (mShouldUseAngle) {
+        updateAngleFeatureOverrides();
+    }
 }
 
 std::string& GraphicsEnv::getPackageName() {
@@ -632,9 +636,25 @@
     return mAngleEglFeatures;
 }
 
+// List of ANGLE features to override (enabled or disable).
+// The list of overrides is loaded and parsed by GpuService.
+void GraphicsEnv::updateAngleFeatureOverrides() {
+    if (!graphicsenv_flags::angle_feature_overrides()) {
+        return;
+    }
+
+    const sp<IGpuService> gpuService = getGpuService();
+    if (!gpuService) {
+        ALOGE("No GPU service");
+        return;
+    }
+
+    mFeatureOverrides = gpuService->getFeatureOverrides();
+}
+
 void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled,
                                            std::vector<const char*>& disabled) {
-    if (!graphicsenv_flags::feature_overrides()) {
+    if (!graphicsenv_flags::angle_feature_overrides()) {
         return;
     }
 
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 42e7c37..9a34aff 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -119,6 +119,21 @@
         }
         return driverPath;
     }
+
+    FeatureOverrides getFeatureOverrides() override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+        FeatureOverrides featureOverrides;
+        status_t error =
+                remote()->transact(BnGpuService::GET_FEATURE_CONFIG_OVERRIDES, data, &reply);
+        if (error != OK) {
+            return featureOverrides;
+        }
+
+        featureOverrides.readFromParcel(&reply);
+        return featureOverrides;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -271,6 +286,15 @@
             toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
             return OK;
         }
+        case GET_FEATURE_CONFIG_OVERRIDES: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            // Get the FeatureOverrides from gpuservice, which implements the IGpuService interface
+            // with GpuService::getFeatureOverrides().
+            FeatureOverrides featureOverrides = getFeatureOverrides();
+            featureOverrides.writeToParcel(reply);
+            return OK;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/graphicsenv/graphicsenv_flags.aconfig b/libs/graphicsenv/graphicsenv_flags.aconfig
index ac66362..efa4bca 100644
--- a/libs/graphicsenv/graphicsenv_flags.aconfig
+++ b/libs/graphicsenv/graphicsenv_flags.aconfig
@@ -2,8 +2,8 @@
 container: "system"
 
 flag {
-  name: "feature_overrides"
-  namespace: "core_graphics"
-  description: "This flag controls the Feature Overrides in GraphicsEnv."
+  name: "angle_feature_overrides"
+  namespace: "gpu"
+  description: "This flag controls the ANGLE Feature Overrides in GraphicsEnv."
   bug: "372694741"
 }
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
index 2b94187..5dc613b 100644
--- a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
+++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
@@ -20,28 +20,35 @@
 #include <string>
 #include <vector>
 
+#include <binder/Parcelable.h>
+
 namespace android {
 
-class FeatureConfig {
+class FeatureConfig : public Parcelable {
 public:
     FeatureConfig() = default;
     FeatureConfig(const FeatureConfig&) = default;
     virtual ~FeatureConfig() = default;
+    virtual status_t writeToParcel(Parcel* parcel) const;
+    virtual status_t readFromParcel(const Parcel* parcel);
     std::string toString() const;
 
     std::string mFeatureName;
     bool mEnabled;
+    std::vector<uint32_t> mGpuVendorIDs;
 };
 
 /*
  * Class for transporting OpenGL ES Feature configurations from GpuService to authorized
  * recipients.
  */
-class FeatureOverrides {
+class FeatureOverrides : public Parcelable {
 public:
     FeatureOverrides() = default;
     FeatureOverrides(const FeatureOverrides&) = default;
     virtual ~FeatureOverrides() = default;
+    virtual status_t writeToParcel(Parcel* parcel) const;
+    virtual status_t readFromParcel(const Parcel* parcel);
     std::string toString() const;
 
     std::vector<FeatureConfig> mGlobalFeatures;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 55fa13a..6821900 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -121,6 +121,7 @@
     // Get the app package name.
     std::string& getPackageName();
     const std::vector<std::string>& getAngleEglFeatures();
+    void updateAngleFeatureOverrides();
     void getAngleFeatureOverrides(std::vector<const char*>& enabled,
                                   std::vector<const char*>& disabled);
     // Set the persist.graphics.egl system property value.
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index a0d6e37..442683a 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -18,6 +18,7 @@
 
 #include <binder/IInterface.h>
 #include <cutils/compiler.h>
+#include <graphicsenv/FeatureOverrides.h>
 #include <graphicsenv/GpuStatsInfo.h>
 
 #include <vector>
@@ -55,6 +56,9 @@
 
     // sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true.
     virtual void toggleAngleAsSystemDriver(bool enabled) = 0;
+
+    // Get the list of features to override.
+    virtual FeatureOverrides getFeatureOverrides() = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
@@ -67,6 +71,7 @@
         TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
         SET_TARGET_STATS_ARRAY,
         ADD_VULKAN_ENGINE_NAME,
+        GET_FEATURE_CONFIG_OVERRIDES,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 5ab31db..158c548 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -281,6 +281,7 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
+        "TransactionState.cpp",
         "VsyncEventData.cpp",
         "view/Surface.cpp",
         "WindowInfosListenerReporter.cpp",
@@ -294,7 +295,6 @@
 cc_defaults {
     name: "libgui-defaults",
     defaults: ["libgui_bufferqueue-defaults"],
-    srcs: [":libgui-sources"],
     static_libs: [
         "libgui_aidl_static",
         "libgui_window_info_static",
@@ -318,6 +318,10 @@
         "libgui-defaults",
     ],
 
+    srcs: [
+        ":libgui-sources",
+    ],
+
     export_static_lib_headers: [
         "libgui_aidl_static",
         "libgui_window_info_static",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c770db9..1aae13c 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -197,15 +197,15 @@
         mUpdateDestinationFrame(updateDestinationFrame) {
     createBufferQueue(&mProducer, &mConsumer);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-    mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
-                                                      GraphicBuffer::USAGE_HW_COMPOSER |
-                                                              GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false, this);
+    mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer,
+                                                            GraphicBuffer::USAGE_HW_COMPOSER |
+                                                                    GraphicBuffer::USAGE_HW_TEXTURE,
+                                                            1, false, this);
 #else
-    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
-                                                      GraphicBuffer::USAGE_HW_COMPOSER |
-                                                              GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false, this);
+    mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer,
+                                                            GraphicBuffer::USAGE_HW_COMPOSER |
+                                                                    GraphicBuffer::USAGE_HW_TEXTURE,
+                                                            1, false, this);
 #endif //  COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     // since the adapter is in the client process, set dequeue timeout
     // explicitly so that dequeueBuffer will block
@@ -222,8 +222,6 @@
     ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
     mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
-    mNumAcquired = 0;
-    mNumFrameAvailable = 0;
 
     TransactionCompletedListener::getInstance()->addQueueStallListener(
             [&](const std::string& reason) {
@@ -439,7 +437,7 @@
 
 void BLASTBufferQueue::flushShadowQueue() {
     BQA_LOGV("flushShadowQueue");
-    int numFramesToFlush = mNumFrameAvailable;
+    int32_t numFramesToFlush = mNumFrameAvailable;
     while (numFramesToFlush > 0) {
         acquireNextBufferLocked(std::nullopt);
         numFramesToFlush--;
@@ -639,7 +637,8 @@
                            bufferItem.mScalingMode, crop);
 
     auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
-    sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+    sp<Fence> fence =
+            bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE;
 
     nsecs_t dequeueTime = -1;
     {
@@ -938,15 +937,22 @@
           : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
 
     void allocateBuffers() override {
+        ATRACE_CALL();
         uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
         uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
         auto gbp = getIGraphicBufferProducer();
-        std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
-                      reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+        std::thread allocateThread([reqWidth, reqHeight, gbp = getIGraphicBufferProducer(),
+                                    reqFormat = mReqFormat, reqUsage = mReqUsage]() {
+            if (com_android_graphics_libgui_flags_allocate_buffer_priority()) {
+                androidSetThreadName("allocateBuffers");
+                pid_t tid = gettid();
+                androidSetThreadPriority(tid, ANDROID_PRIORITY_DISPLAY);
+            }
+
             gbp->allocateBuffers(reqWidth, reqHeight,
                                  reqFormat, reqUsage);
-
-        }).detach();
+        });
+        allocateThread.detach();
     }
 
     status_t setFrameRate(float frameRate, int8_t compatibility,
@@ -1016,7 +1022,7 @@
     if (includeSurfaceControlHandle && mSurfaceControl) {
         scHandle = mSurfaceControl->getHandle();
     }
-    return new BBQSurface(mProducer, true, scHandle, this);
+    return sp<BBQSurface>::make(mProducer, true, scHandle, this);
 }
 
 void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
@@ -1122,10 +1128,10 @@
 class AsyncProducerListener : public BnProducerListener {
 private:
     const sp<IProducerListener> mListener;
+    AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+    friend class sp<AsyncProducerListener>;
 
 public:
-    AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
-
     void onBufferReleased() override {
         AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
     }
@@ -1179,7 +1185,7 @@
             return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
         }
 
-        return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+        return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api,
                                             producerControlledByApp, output);
     }
 
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 3b2d337..9dcd5dc 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -215,14 +215,14 @@
     FlattenableUtils::read(buffer, size, flags);
 
     if (flags & 1) {
-        mGraphicBuffer = new GraphicBuffer();
+        mGraphicBuffer = sp<GraphicBuffer>::make();
         status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
     }
 
     if (flags & 2) {
-        mFence = new Fence();
+        mFence = sp<Fence>::make();
         status_t err = mFence->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 8566419..4926ceb 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "BufferItemConsumer"
 //#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Errors.h>
 #include <utils/Log.h>
 
 #include <inttypes.h>
@@ -24,6 +25,7 @@
 #include <com_android_graphics_libgui_flags.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
 #include <ui/BufferQueueDefs.h>
 #include <ui/GraphicBuffer.h>
 
@@ -35,6 +37,30 @@
 
 namespace android {
 
+std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create(
+        uint64_t consumerUsage, int bufferCount, bool controlledByApp,
+        bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<BufferItemConsumer> bufferItemConsumer =
+            sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp,
+                                         isConsumerSurfaceFlinger);
+    return {bufferItemConsumer, bufferItemConsumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+    sp<BufferItemConsumer> bufferItemConsumer =
+            sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp);
+    return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer,
+                                                  uint64_t consumerUsage, int bufferCount,
+                                                  bool controlledByApp) {
+    return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
                                        bool controlledByApp, bool isConsumerSurfaceFlinger)
@@ -107,13 +133,36 @@
     return OK;
 }
 
+status_t BufferItemConsumer::attachBuffer(const sp<GraphicBuffer>& buffer) {
+    if (!buffer) {
+        BI_LOGE("BufferItemConsumer::attachBuffer no input buffer specified.");
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _l(mMutex);
+
+    int slot = INVALID_BUFFER_SLOT;
+    status_t status = mConsumer->attachBuffer(&slot, buffer);
+    if (status != OK) {
+        BI_LOGE("BufferItemConsumer::attachBuffer unable to attach buffer %d", status);
+        return status;
+    }
+
+    mSlots[slot] = {
+            .mGraphicBuffer = buffer,
+            .mFence = nullptr,
+            .mFrameNumber = 0,
+    };
+
+    return OK;
+}
+
 status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
         const sp<Fence>& releaseFence) {
     Mutex::Autolock _l(mMutex);
     return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
 }
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer,
                                            const sp<Fence>& releaseFence) {
     Mutex::Autolock _l(mMutex);
@@ -129,7 +178,6 @@
 
     return releaseBufferSlotLocked(slotIndex, buffer, releaseFence);
 }
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
                                                      const sp<Fence>& releaseFence) {
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index f1374e2..f21ac18 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -125,15 +125,16 @@
     LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
             "BufferQueue: outConsumer must not be NULL");
 
-    sp<BufferQueueCore> core(new BufferQueueCore());
+    sp<BufferQueueCore> core = sp<BufferQueueCore>::make();
     LOG_ALWAYS_FATAL_IF(core == nullptr,
             "BufferQueue: failed to create BufferQueueCore");
 
-    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
+    sp<IGraphicBufferProducer> producer =
+            sp<BufferQueueProducer>::make(core, consumerIsSurfaceFlinger);
     LOG_ALWAYS_FATAL_IF(producer == nullptr,
             "BufferQueue: failed to create BufferQueueProducer");
 
-    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+    sp<IGraphicBufferConsumer> consumer = sp<BufferQueueConsumer>::make(core);
     LOG_ALWAYS_FATAL_IF(consumer == nullptr,
             "BufferQueue: failed to create BufferQueueConsumer");
 
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 270bfbd..4681c9e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
-#include <pwd.h>
-#include <sys/types.h>
-
 #define LOG_TAG "BufferQueueConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
@@ -48,6 +44,11 @@
 
 #include <com_android_graphics_libgui_flags.h>
 
+#include <inttypes.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <optional>
+
 namespace android {
 
 // Macros for include BufferQueueCore information in log messages
@@ -767,11 +768,15 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+    return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt);
+}
+
 status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
-        int maxAcquiredBuffers) {
+        int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) {
     ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
 
-    sp<IConsumerListener> listener;
+    std::optional<OnBufferReleasedCallback> callback;
     { // Autolock scope
         std::unique_lock<std::mutex> lock(mCore->mMutex);
 
@@ -833,13 +838,20 @@
         BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
         mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
         VALIDATE_CONSISTENCY();
-        if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
-            listener = mCore->mConsumerListener;
+        if (delta < 0) {
+            if (onBuffersReleasedCallback) {
+                callback = std::move(onBuffersReleasedCallback);
+            } else if (mCore->mBufferReleasedCbEnabled) {
+                callback = [listener = mCore->mConsumerListener]() {
+                    listener->onBuffersReleased();
+                };
+            }
         }
     }
+
     // Call back without lock held
-    if (listener != nullptr) {
-        listener->onBuffersReleased();
+    if (callback) {
+        (*callback)();
     }
 
     return NO_ERROR;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index c241482..bcf61e4 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -364,8 +364,10 @@
         // Producers are not allowed to dequeue more than
         // mMaxDequeuedBufferCount buffers.
         // This check is only done if a buffer has already been queued
-        if (mCore->mBufferHasBeenQueued &&
-                dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
+        using namespace com::android::graphics::libgui::flags;
+        bool flagGatedBufferHasBeenQueued =
+                bq_always_use_max_dequeued_buffer_count() || mCore->mBufferHasBeenQueued;
+        if (flagGatedBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
             // Supress error logs when timeout is non-negative.
             if (mDequeueTimeout < 0) {
                 BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
@@ -693,11 +695,11 @@
                 .requestorName = {mConsumerName.c_str(), mConsumerName.size()},
                 .extras = std::move(tempOptions),
         };
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+        sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
 #else
         sp<GraphicBuffer> graphicBuffer =
-                new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
-                                  {mConsumerName.c_str(), mConsumerName.size()});
+                sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage,
+                                        std::string{mConsumerName.c_str(), mConsumerName.size()});
 #endif
 
         status_t error = graphicBuffer->initCheck();
@@ -1464,7 +1466,7 @@
 #ifndef NO_BINDER
                 if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                     status = IInterface::asBinder(listener)->linkToDeath(
-                            static_cast<IBinder::DeathRecipient*>(this));
+                            sp<IBinder::DeathRecipient>::fromExisting(this));
                     if (status != NO_ERROR) {
                         BQ_LOGE("connect: linkToDeath failed: %s (%d)",
                                 strerror(-status), status);
@@ -1553,8 +1555,7 @@
                                 IInterface::asBinder(mCore->mLinkedToDeath);
                         // This can fail if we're here because of the death
                         // notification, but we just ignore it
-                        token->unlinkToDeath(
-                                static_cast<IBinder::DeathRecipient*>(this));
+                        token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this));
                     }
 #endif
                     mCore->mSharedBufferSlot =
@@ -1685,11 +1686,11 @@
 #endif
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+        sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
 #else
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
-                allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
-                allocUsage, allocName);
+        sp<GraphicBuffer> graphicBuffer =
+                sp<GraphicBuffer>::make(allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                                        allocUsage, allocName);
 #endif
 
         status_t result = graphicBuffer->initCheck();
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9cb013..4f495d0 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -108,7 +108,7 @@
 
 status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
                                                   int const*& fds, size_t& count) {
-    releaseFence = new Fence();
+    releaseFence = sp<Fence>::make();
     if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
         return err;
     }
@@ -344,4 +344,4 @@
     return STATUS_OK;
 }
 
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index ab747b9..80a3543 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -20,6 +20,7 @@
 #include <gui/Choreographer.h>
 #include <gui/TraceUtils.h>
 #include <jni.h>
+#include <utils/Looper.h>
 
 #undef LOG_TAG
 #define LOG_TAG "AChoreographer"
@@ -69,7 +70,7 @@
 
 Choreographer::Context Choreographer::gChoreographers;
 
-static thread_local Choreographer* gChoreographer;
+static thread_local sp<Choreographer> gChoreographer;
 
 void Choreographer::initJVM(JNIEnv* env) {
     env->GetJavaVM(&gJni.jvm);
@@ -86,14 +87,14 @@
                              "()V");
 }
 
-Choreographer* Choreographer::getForThread() {
+sp<Choreographer> Choreographer::getForThread() {
     if (gChoreographer == nullptr) {
         sp<Looper> looper = Looper::getForThread();
         if (!looper.get()) {
             ALOGW("No looper prepared for thread");
             return nullptr;
         }
-        gChoreographer = new Choreographer(looper);
+        gChoreographer = sp<Choreographer>::make(looper);
         status_t result = gChoreographer->initialize();
         if (result != OK) {
             ALOGW("Failed to initialize");
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 67de742..5b89c6e 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -31,6 +31,7 @@
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
 #include <gui/ConsumerBase.h>
+#include <gui/IConsumerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -68,8 +69,8 @@
 #endif
         mAbandoned(false),
         mConsumer(bufferQueue),
-        mPrevFinalReleaseFence(Fence::NO_FENCE) {
-    initialize(controlledByApp);
+        mPrevFinalReleaseFence(Fence::NO_FENCE),
+        mIsControlledByApp(controlledByApp) {
 }
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
@@ -79,11 +80,11 @@
         mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
 #endif
         mAbandoned(false),
-        mPrevFinalReleaseFence(Fence::NO_FENCE) {
+        mPrevFinalReleaseFence(Fence::NO_FENCE),
+        mIsControlledByApp(controlledByApp) {
     sp<IGraphicBufferProducer> producer;
     BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
     mSurface = sp<Surface>::make(producer, controlledByApp);
-    initialize(controlledByApp);
 }
 
 ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
@@ -95,24 +96,27 @@
         mAbandoned(false),
         mConsumer(consumer),
         mSurface(sp<Surface>::make(producer, controlledByApp)),
-        mPrevFinalReleaseFence(Fence::NO_FENCE) {
-    initialize(controlledByApp);
+        mPrevFinalReleaseFence(Fence::NO_FENCE),
+        mIsControlledByApp(controlledByApp) {
 }
 
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
-void ConsumerBase::initialize(bool controlledByApp) {
+void ConsumerBase::onFirstRef() {
+    ConsumerListener::onFirstRef();
+    initialize();
+}
+
+void ConsumerBase::initialize() {
     // Choose a name using the PID and a process-unique ID.
     mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
 
-    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
-    // reference once the ctor ends, as that would cause the refcount of 'this'
-    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
-    // that's what we create.
-    wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
-    sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
+    // Here we depend on an sp/wp having been created for `this`.  For this
+    // reason, initialize() cannot be called from a ctor.
+    wp<ConsumerListener> listener = wp<ConsumerListener>::fromExisting(this);
+    sp<IConsumerListener> proxy = sp<BufferQueue::ProxyConsumerListener>::make(listener);
 
-    status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
+    status_t err = mConsumer->consumerConnect(proxy, mIsControlledByApp);
     if (err != NO_ERROR) {
         CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
                 strerror(-err), err);
@@ -260,7 +264,10 @@
 
 void ConsumerBase::onBuffersReleased() {
     Mutex::Autolock lock(mMutex);
+    onBuffersReleasedLocked();
+}
 
+void ConsumerBase::onBuffersReleasedLocked() {
     CB_LOGV("onBuffersReleased");
 
     if (mAbandoned) {
@@ -477,10 +484,10 @@
         CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!");
         return NO_INIT;
     }
-    return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
+    return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers,
+                                                {[this]() { onBuffersReleasedLocked(); }});
 }
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
@@ -489,7 +496,6 @@
     }
     return mConsumer->setConsumerIsProtected(isProtected);
 }
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
 sp<NativeHandle> ConsumerBase::getSidebandStream() const {
     Mutex::Autolock _l(mMutex);
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 23b432e..ecbceb7 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -20,7 +20,11 @@
 
 #include <com_android_graphics_libgui_flags.h>
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 #include <utils/Log.h>
 
 #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,6 +35,28 @@
 
 namespace android {
 
+std::tuple<sp<CpuConsumer>, sp<Surface>> CpuConsumer::create(size_t maxLockedBuffers,
+                                                             bool controlledByApp,
+                                                             bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<CpuConsumer> consumer =
+            sp<CpuConsumer>::make(maxLockedBuffers, controlledByApp, isConsumerSurfaceFlinger);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+
+    return {sp<CpuConsumer>::make(igbc, maxLockedBuffers, controlledByApp),
+            sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<CpuConsumer> CpuConsumer::create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                                    bool controlledByApp) {
+    return sp<CpuConsumer>::make(bq, maxLockedBuffers, controlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
                          bool isConsumerSurfaceFlinger)
@@ -230,7 +256,7 @@
         return err;
     }
 
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence);
     releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer);
 
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 052b8ed..2c5770d 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -37,6 +37,7 @@
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/GLConsumer.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
 #include <private/gui/ComposerService.h>
@@ -101,6 +102,50 @@
     return hasIt;
 }
 
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t tex, uint32_t textureTarget,
+                                                           bool useFenceSync,
+                                                           bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<GLConsumer> consumer =
+            sp<GLConsumer>::make(tex, textureTarget, useFenceSync, isControlledByApp);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc);
+
+    return {sp<GLConsumer>::make(igbc, tex, textureTarget, useFenceSync, isControlledByApp),
+            sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t textureTarget,
+                                                           bool useFenceSync,
+                                                           bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<GLConsumer> consumer = sp<GLConsumer>::make(textureTarget, useFenceSync, isControlledByApp);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc);
+
+    return {sp<GLConsumer>::make(igbc, textureTarget, useFenceSync, isControlledByApp),
+            sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                                  uint32_t textureTarget, bool useFenceSync,
+                                  bool isControlledByApp) {
+    return sp<GLConsumer>::make(bq, tex, textureTarget, useFenceSync, isControlledByApp);
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+                                  bool useFenceSync, bool isControlledByApp) {
+    return sp<GLConsumer>::make(bq, textureTarget, useFenceSync, isControlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
       : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
@@ -333,7 +378,7 @@
         }
 
         if (mReleasedTexImage == nullptr) {
-            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+            mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer());
         }
 
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
@@ -365,10 +410,10 @@
     if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
         // The first time, create the debug texture in case the application
         // continues to use it.
-        sp<GraphicBuffer> buffer = new GraphicBuffer(
-                kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
-                DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
-                "[GLConsumer debug texture]");
+        sp<GraphicBuffer> buffer =
+                sp<GraphicBuffer>::make(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+                                        DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
+                                        "[GLConsumer debug texture]");
         uint32_t* bits;
         buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
         uint32_t stride = buffer->getStride();
@@ -400,7 +445,7 @@
     // replaces any old EglImage with a new one (using the new buffer).
     if (item->mGraphicBuffer != nullptr) {
         int slot = item->mSlot;
-        mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
+        mEglSlots[slot].mEglImage = sp<EglImage>::make(item->mGraphicBuffer);
     }
 
     return NO_ERROR;
@@ -737,7 +782,7 @@
                         "fd: %#x", eglGetError());
                 return UNKNOWN_ERROR;
             }
-            sp<Fence> fence(new Fence(fenceFd));
+            sp<Fence> fence = sp<Fence>::make(fenceFd);
             status_t err = addReleaseFenceLocked(mCurrentTexture,
                     mCurrentTextureImage->graphicBuffer(), fence);
             if (err != OK) {
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 9f71eb1..1d1910e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -104,7 +104,7 @@
         }
         bool nonNull = reply.readInt32();
         if (nonNull) {
-            *buf = new GraphicBuffer();
+            *buf = sp<GraphicBuffer>::make();
             result = reply.read(**buf);
             if(result != NO_ERROR) {
                 (*buf).clear();
@@ -197,7 +197,7 @@
         }
 
         *buf = reply.readInt32();
-        *fence = new Fence();
+        *fence = sp<Fence>::make();
         result = reply.read(**fence);
         if (result != NO_ERROR) {
             fence->clear();
@@ -293,7 +293,7 @@
         if (result == NO_ERROR) {
             bool nonNull = reply.readInt32();
             if (nonNull) {
-                *outBuffer = new GraphicBuffer;
+                *outBuffer = sp<GraphicBuffer>::make();
                 result = reply.read(**outBuffer);
                 if (result != NO_ERROR) {
                     outBuffer->clear();
@@ -302,7 +302,7 @@
             }
             nonNull = reply.readInt32();
             if (nonNull) {
-                *outFence = new Fence;
+                *outFence = sp<Fence>::make();
                 result = reply.read(**outFence);
                 if (result != NO_ERROR) {
                     outBuffer->clear();
@@ -640,7 +640,7 @@
         bool hasBuffer = reply.readBool();
         sp<GraphicBuffer> buffer;
         if (hasBuffer) {
-            buffer = new GraphicBuffer();
+            buffer = sp<GraphicBuffer>::make();
             result = reply.read(*buffer);
             if (result == NO_ERROR) {
                 result = reply.read(outTransformMatrix, sizeof(float) * 16);
@@ -650,7 +650,7 @@
             ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
             return result;
         }
-        sp<Fence> fence(new Fence);
+        sp<Fence> fence = sp<Fence>::make();
         result = reply.read(*fence);
         if (result != NO_ERROR) {
             ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -687,7 +687,7 @@
         }
         sp<GraphicBuffer> buffer;
         if (hasBuffer) {
-            buffer = new GraphicBuffer();
+            buffer = sp<GraphicBuffer>::make();
             result = reply.read(*buffer);
             if (result == NO_ERROR) {
                 result = reply.read(*outRect);
@@ -700,7 +700,7 @@
             ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
             return result;
         }
-        sp<Fence> fence(new Fence);
+        sp<Fence> fence = sp<Fence>::make();
         result = reply.read(*fence);
         if (result != NO_ERROR) {
             ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -1232,7 +1232,7 @@
         }
         case ATTACH_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
-            sp<GraphicBuffer> buffer = new GraphicBuffer();
+            sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
             status_t result = data.read(*buffer.get());
             int slot = 0;
             if (result == NO_ERROR) {
@@ -1250,7 +1250,7 @@
                 return result;
             }
             for (sp<GraphicBuffer>& buffer : buffers) {
-                buffer = new GraphicBuffer();
+                buffer = sp<GraphicBuffer>::make();
                 result = data.read(*buffer.get());
                 if (result != NO_ERROR) {
                     return result;
@@ -1306,7 +1306,7 @@
         case CANCEL_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int buf = data.readInt32();
-            sp<Fence> fence = new Fence();
+            sp<Fence> fence = sp<Fence>::make();
             status_t result = data.read(*fence.get());
             if (result == NO_ERROR) {
                 result = cancelBuffer(buf, fence);
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index 8b2e2dd..393e1c3 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -105,7 +105,7 @@
             : std::nullopt;
 #endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     status_t result = fence->unflatten(buffer, size, fds, count);
     if (result != NO_ERROR) {
         return result;
@@ -228,7 +228,7 @@
     FlattenableUtils::read(fBuffer, size, result);
     int32_t isBufferNull = 0;
     FlattenableUtils::read(fBuffer, size, isBufferNull);
-    buffer = new GraphicBuffer();
+    buffer = sp<GraphicBuffer>::make();
     if (!isBufferNull) {
         status_t status = buffer->unflatten(fBuffer, size, fds, count);
         if (status != NO_ERROR) {
@@ -323,7 +323,7 @@
     FlattenableUtils::read(buffer, size, slot);
     FlattenableUtils::read(buffer, size, bufferAge);
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     status_t status = fence->unflatten(buffer, size, fds, count);
     if (status != NO_ERROR) {
         return status;
@@ -395,7 +395,7 @@
 
     FlattenableUtils::read(buffer, size, slot);
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     return fence->unflatten(buffer, size, fds, count);
 }
 
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 83fc827..ed28e79 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -92,7 +92,7 @@
     if (err != NO_ERROR) return err;
 
     if (hasFence) {
-        gpuCompositionDoneFence = new Fence();
+        gpuCompositionDoneFence = sp<Fence>::make();
         err = input->read(*gpuCompositionDoneFence);
         if (err != NO_ERROR) return err;
     }
@@ -157,7 +157,7 @@
 
     SAFE_PARCEL(input->readBool, &hasFence);
     if (hasFence) {
-        previousReleaseFence = new Fence();
+        previousReleaseFence = sp<Fence>::make();
         SAFE_PARCEL(input->read, *previousReleaseFence);
     }
     bool hasTransformHint = false;
@@ -216,7 +216,7 @@
         return err;
     }
     if (hasFence) {
-        presentFence = new Fence();
+        presentFence = sp<Fence>::make();
         err = input->read(*presentFence);
         if (err != NO_ERROR) {
             return err;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index ebfc62f..ad95d1a 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,6 +55,28 @@
 using gui::FocusRequest;
 using gui::WindowInfoHandle;
 
+namespace {
+bool isSameWindowHandle(const sp<WindowInfoHandle>& lhs, const sp<WindowInfoHandle>& rhs) {
+    if (lhs == rhs) {
+        return true;
+    }
+
+    if (!lhs || !rhs) {
+        return false;
+    }
+
+    return *lhs->getInfo() == *rhs->getInfo();
+};
+
+bool isSameSurfaceControl(const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) {
+    if (lhs == rhs) {
+        return true;
+    }
+
+    return SurfaceControl::isSameSurface(lhs, rhs);
+};
+} // namespace
+
 layer_state_t::layer_state_t()
       : surface(nullptr),
         layerId(-1),
@@ -73,7 +95,6 @@
         transformToDisplayInverse(false),
         crop({0, 0, -1, -1}),
         dataspace(ui::Dataspace::UNKNOWN),
-        surfaceDamageRegion(),
         api(-1),
         colorTransform(mat4()),
         bgColor(0),
@@ -117,19 +138,21 @@
     SAFE_PARCEL(output.writeFloat, crop.left);
     SAFE_PARCEL(output.writeFloat, crop.bottom);
     SAFE_PARCEL(output.writeFloat, crop.right);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+                mNotDefCmpState.relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+                mNotDefCmpState.parentSurfaceControlForChild);
     SAFE_PARCEL(output.writeFloat, color.r);
     SAFE_PARCEL(output.writeFloat, color.g);
     SAFE_PARCEL(output.writeFloat, color.b);
     SAFE_PARCEL(output.writeFloat, color.a);
-    SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
-    SAFE_PARCEL(output.write, transparentRegion);
+    SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->writeToParcel, &output);
+    SAFE_PARCEL(output.write, mNotDefCmpState.transparentRegion);
     SAFE_PARCEL(output.writeUint32, bufferTransform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
     SAFE_PARCEL(output.write, hdrMetadata);
-    SAFE_PARCEL(output.write, surfaceDamageRegion);
+    SAFE_PARCEL(output.write, mNotDefCmpState.surfaceDamageRegion);
     SAFE_PARCEL(output.writeInt32, api);
 
     if (sidebandStream) {
@@ -241,8 +264,10 @@
     SAFE_PARCEL(input.readFloat, &crop.bottom);
     SAFE_PARCEL(input.readFloat, &crop.right);
 
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+                &mNotDefCmpState.relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+                &mNotDefCmpState.parentSurfaceControlForChild);
 
     float tmpFloat = 0;
     SAFE_PARCEL(input.readFloat, &tmpFloat);
@@ -254,9 +279,9 @@
     SAFE_PARCEL(input.readFloat, &tmpFloat);
     color.a = tmpFloat;
 
-    SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
+    SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->readFromParcel, &input);
 
-    SAFE_PARCEL(input.read, transparentRegion);
+    SAFE_PARCEL(input.read, mNotDefCmpState.transparentRegion);
     SAFE_PARCEL(input.readUint32, &bufferTransform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
 
@@ -265,7 +290,7 @@
     dataspace = static_cast<ui::Dataspace>(tmpUint32);
 
     SAFE_PARCEL(input.read, hdrMetadata);
-    SAFE_PARCEL(input.read, surfaceDamageRegion);
+    SAFE_PARCEL(input.read, mNotDefCmpState.surfaceDamageRegion);
     SAFE_PARCEL(input.readInt32, &api);
 
     bool tmpBool = false;
@@ -583,7 +608,7 @@
     }
     if (other.what & eTransparentRegionChanged) {
         what |= eTransparentRegionChanged;
-        transparentRegion = other.transparentRegion;
+        mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion;
     }
     if (other.what & eFlagsChanged) {
         what |= eFlagsChanged;
@@ -615,11 +640,13 @@
         what |= eRelativeLayerChanged;
         what &= ~eLayerChanged;
         z = other.z;
-        relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
+        mNotDefCmpState.relativeLayerSurfaceControl =
+                other.mNotDefCmpState.relativeLayerSurfaceControl;
     }
     if (other.what & eReparent) {
         what |= eReparent;
-        parentSurfaceControlForChild = other.parentSurfaceControlForChild;
+        mNotDefCmpState.parentSurfaceControlForChild =
+                other.mNotDefCmpState.parentSurfaceControlForChild;
     }
     if (other.what & eBufferTransformChanged) {
         what |= eBufferTransformChanged;
@@ -665,7 +692,7 @@
     }
     if (other.what & eSurfaceDamageRegionChanged) {
         what |= eSurfaceDamageRegionChanged;
-        surfaceDamageRegion = other.surfaceDamageRegion;
+        mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion;
     }
     if (other.what & eApiChanged) {
         what |= eApiChanged;
@@ -684,7 +711,8 @@
     }
     if (other.what & eInputInfoChanged) {
         what |= eInputInfoChanged;
-        windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
+        mNotDefCmpState.windowInfoHandle =
+                sp<WindowInfoHandle>::make(*other.mNotDefCmpState.windowInfoHandle);
     }
     if (other.what & eBackgroundColorChanged) {
         what |= eBackgroundColorChanged;
@@ -807,7 +835,8 @@
     CHECK_DIFF(diff, eAlphaChanged, other, color.a);
     CHECK_DIFF(diff, eMatrixChanged, other, matrix);
     if (other.what & eTransparentRegionChanged &&
-        (!transparentRegion.hasSameRects(other.transparentRegion))) {
+        (!mNotDefCmpState.transparentRegion.hasSameRects(
+                other.mNotDefCmpState.transparentRegion))) {
         diff |= eTransparentRegionChanged;
     }
     if (other.what & eFlagsChanged) {
@@ -824,8 +853,8 @@
         diff &= ~eLayerChanged;
     }
     if (other.what & eReparent &&
-        !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
-                                       other.parentSurfaceControlForChild)) {
+        !SurfaceControl::isSameSurface(mNotDefCmpState.parentSurfaceControlForChild,
+                                       other.mNotDefCmpState.parentSurfaceControlForChild)) {
         diff |= eReparent;
     }
     CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
@@ -839,7 +868,8 @@
     CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint);
     CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
     if (other.what & eSurfaceDamageRegionChanged &&
-        (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+        (!mNotDefCmpState.surfaceDamageRegion.hasSameRects(
+                other.mNotDefCmpState.surfaceDamageRegion))) {
         diff |= eSurfaceDamageRegionChanged;
     }
     CHECK_DIFF(diff, eApiChanged, other, api);
@@ -901,6 +931,38 @@
     SAFE_PARCEL(input.readFloat, &dsdy);
     return NO_ERROR;
 }
+void layer_state_t::updateTransparentRegion(const Region& transparentRegion) {
+    what |= eTransparentRegionChanged;
+    mNotDefCmpState.transparentRegion = transparentRegion;
+}
+void layer_state_t::updateSurfaceDamageRegion(const Region& surfaceDamageRegion) {
+    what |= eSurfaceDamageRegionChanged;
+    mNotDefCmpState.surfaceDamageRegion = surfaceDamageRegion;
+}
+void layer_state_t::updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z) {
+    what |= layer_state_t::eRelativeLayerChanged;
+    what &= ~layer_state_t::eLayerChanged;
+    mNotDefCmpState.relativeLayerSurfaceControl = relativeTo;
+    this->z = z;
+}
+void layer_state_t::updateParentLayer(const sp<SurfaceControl>& newParent) {
+    what |= layer_state_t::eReparent;
+    mNotDefCmpState.parentSurfaceControlForChild =
+            newParent ? newParent->getParentingLayer() : nullptr;
+}
+void layer_state_t::updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info) {
+    what |= eInputInfoChanged;
+    mNotDefCmpState.windowInfoHandle = std::move(info);
+}
+
+bool layer_state_t::NotDefaultComparableState::operator==(
+        const NotDefaultComparableState& rhs) const {
+    return transparentRegion.hasSameRects(rhs.transparentRegion) &&
+            surfaceDamageRegion.hasSameRects(rhs.surfaceDamageRegion) &&
+            isSameWindowHandle(windowInfoHandle, rhs.windowInfoHandle) &&
+            isSameSurfaceControl(relativeLayerSurfaceControl, rhs.relativeLayerSurfaceControl) &&
+            isSameSurfaceControl(parentSurfaceControlForChild, rhs.parentSurfaceControlForChild);
+}
 
 // ------------------------------- InputWindowCommands ----------------------------------------
 
@@ -1001,13 +1063,13 @@
     bool tmpBool = false;
     SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
-        buffer = new GraphicBuffer();
+        buffer = sp<GraphicBuffer>::make();
         SAFE_PARCEL(input->read, *buffer);
     }
 
     SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
-        acquireFence = new Fence();
+        acquireFence = sp<Fence>::make();
         SAFE_PARCEL(input->read, *acquireFence);
     }
 
@@ -1034,8 +1096,8 @@
 }
 
 status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const {
-    SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface);
-    SAFE_PARCEL(parcel->writeInt32, callbackId);
+    SAFE_PARCEL(parcel->writeStrongBinder, mState.callbackInterface);
+    SAFE_PARCEL(parcel->writeInt32, mState.callbackId);
     return NO_ERROR;
 }
 
@@ -1043,9 +1105,9 @@
     sp<IBinder> tmpBinder = nullptr;
     SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder);
     if (tmpBinder) {
-        callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+        mState.callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
     }
-    SAFE_PARCEL(parcel->readInt32, &callbackId);
+    SAFE_PARCEL(parcel->readInt32, &mState.callbackId);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index 2de023e..30b5785 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -18,6 +18,7 @@
 
 #include <private/gui/ParcelUtils.h>
 #include <ui/FenceResult.h>
+#include <ui/GraphicBuffer.h>
 
 namespace android::gui {
 
@@ -54,7 +55,7 @@
     bool hasGraphicBuffer;
     SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
     if (hasGraphicBuffer) {
-        buffer = new GraphicBuffer();
+        buffer = sp<GraphicBuffer>::make();
         SAFE_PARCEL(parcel->read, *buffer);
     }
 
@@ -79,7 +80,7 @@
     bool hasGainmap;
     SAFE_PARCEL(parcel->readBool, &hasGainmap);
     if (hasGainmap) {
-        optionalGainMap = new GraphicBuffer();
+        optionalGainMap = sp<GraphicBuffer>::make();
         SAFE_PARCEL(parcel->read, *optionalGainMap);
     }
     SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio);
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 653b91b..848ae7a 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -47,7 +47,7 @@
         return BAD_VALUE;
     }
 
-    sp<StreamSplitter> splitter(new StreamSplitter(inputQueue));
+    sp<StreamSplitter> splitter = sp<StreamSplitter>::make(inputQueue);
     status_t status = splitter->mInput->consumerConnect(splitter, false);
     if (status == NO_ERROR) {
         splitter->mInput->setConsumerName(String8("StreamSplitter"));
@@ -82,7 +82,7 @@
     Mutex::Autolock lock(mMutex);
 
     IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-    sp<OutputListener> listener(new OutputListener(this, outputQueue));
+    sp<OutputListener> listener = sp<OutputListener>::make(this, outputQueue);
     IInterface::asBinder(outputQueue)->linkToDeath(listener);
     status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU,
             /* producerControlledByApp */ false, &queueBufferOutput);
@@ -140,7 +140,7 @@
 
     // Initialize our reference count for this buffer
     mBuffers.add(bufferItem.mGraphicBuffer->getId(),
-            new BufferTracker(bufferItem.mGraphicBuffer));
+                 sp<BufferTracker>::make(bufferItem.mGraphicBuffer));
 
     IGraphicBufferProducer::QueueBufferInput queueInput(
             bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ec23365..63dcfbc 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -509,7 +509,7 @@
     if (result != OK) {
         return result;
     }
-    sp<Fence> fence(new Fence(fenceFd));
+    sp<Fence> fence = sp<Fence>::make(fenceFd);
     int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
     if (waitResult != OK) {
         ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
@@ -979,7 +979,7 @@
         }
         return OK;
     }
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     mGraphicBufferProducer->cancelBuffer(i, fence);
 
     if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
@@ -1017,7 +1017,7 @@
             ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
             badSlotResult = slot;
         } else {
-            sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+            sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
             cancelBufferInputs[numBuffersCancelled].slot = slot;
             cancelBufferInputs[numBuffersCancelled++].fence = fence;
         }
@@ -1078,7 +1078,7 @@
     Rect crop(Rect::EMPTY_RECT);
     mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
 
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
             mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -2092,7 +2092,7 @@
 }
 
 int Surface::connect(int api) {
-    static sp<SurfaceListener> listener = new StubSurfaceListener();
+    static sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
     return connect(api, listener);
 }
 
@@ -2104,7 +2104,7 @@
     mReportRemovedBuffers = reportBufferRemoval;
 
     if (listener != nullptr) {
-        mListenerProxy = new ProducerListenerProxy(this, listener);
+        mListenerProxy = sp<ProducerListenerProxy>::make(this, listener);
     }
 
     int err =
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 37ed23b..9854274 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -123,7 +123,7 @@
         explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
     };
 
-    mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
+    mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerService*>(this));
     IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
     return true;
 }
@@ -170,7 +170,7 @@
         explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {}
     };
 
-    mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this));
+    mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerServiceAIDL*>(this));
     IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
     return true;
 }
@@ -202,7 +202,7 @@
         DefaultComposerClient& dc = DefaultComposerClient::getInstance();
         Mutex::Autolock _l(dc.mLock);
         if (dc.mClient == nullptr) {
-            dc.mClient = new SurfaceComposerClient;
+            dc.mClient = sp<SurfaceComposerClient>::make();
         }
         return dc.mClient;
     }
@@ -399,7 +399,7 @@
 sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
     std::lock_guard<std::mutex> lock(sListenerInstanceMutex);
     if (sInstance == nullptr) {
-        sInstance = new TransactionCompletedListener;
+        sInstance = sp<TransactionCompletedListener>::make();
     }
     return sInstance;
 }
@@ -691,7 +691,7 @@
     std::scoped_lock<std::mutex> lock(mMutex);
     mTrustedPresentationCallbacks[id] =
             std::tuple<TrustedPresentationCallback, void*>(tpc, context);
-    return new SurfaceComposerClient::PresentationCallbackRAII(this, id);
+    return sp<SurfaceComposerClient::PresentationCallbackRAII>::make(this, id);
 }
 
 void TransactionCompletedListener::clearTrustedPresentationCallback(int id) {
@@ -743,7 +743,7 @@
  */
 class BufferCache : public Singleton<BufferCache> {
 public:
-    BufferCache() : token(new BBinder()) {}
+    BufferCache() : token(sp<BBinder>::make()) {}
 
     sp<IBinder> getToken() {
         return IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -1357,7 +1357,7 @@
     return binderStatus;
 }
 
-sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make();
 
 std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex;
 
@@ -1391,11 +1391,16 @@
 // ---------------------------------------------------------------------------
 
 sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
-                                                        bool isSecure, const std::string& uniqueId,
+                                                        bool isSecure, bool optimizeForPower,
+                                                        const std::string& uniqueId,
                                                         float requestedRefreshRate) {
+    const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower
+            ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower
+            : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
     sp<IBinder> display = nullptr;
     binder::Status status =
             ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure,
+                                                                            optimizationPolicy,
                                                                             uniqueId,
                                                                             requestedRefreshRate,
                                                                             &display);
@@ -1523,11 +1528,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eRelativeLayerChanged;
-    s->what &= ~layer_state_t::eLayerChanged;
-    s->relativeLayerSurfaceControl = relativeTo;
-    s->z = z;
-
+    s->updateRelativeLayer(relativeTo, z);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1557,9 +1558,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eTransparentRegionChanged;
-    s->transparentRegion = transparentRegion;
-
+    s->updateTransparentRegion(transparentRegion);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1721,9 +1720,7 @@
     if (SurfaceControl::isSameSurface(sc, newParent)) {
         return *this;
     }
-    s->what |= layer_state_t::eReparent;
-    s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
-
+    s->updateParentLayer(newParent);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1953,9 +1950,9 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
-        const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
-        const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions,
-        const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) {
+        const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets,
+        const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes,
+        const std::vector<int32_t>& samplingKeys) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1964,8 +1961,8 @@
 
     s->what |= layer_state_t::eLutsChanged;
     if (lutFd.ok()) {
-        s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
-                                                     dimensions, sizes, samplingKeys);
+        s->luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), offsets, dimensions, sizes,
+                                                     samplingKeys);
     } else {
         s->luts = nullptr;
     }
@@ -2009,9 +2006,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eSurfaceDamageRegionChanged;
-    s->surfaceDamageRegion = surfaceDamageRegion;
-
+    s->updateSurfaceDamageRegion(surfaceDamageRegion);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -2130,21 +2125,20 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->windowInfoHandle = std::move(info);
-    s->what |= layer_state_t::eInputInfoChanged;
+    s->updateInputWindowInfo(std::move(info));
     return *this;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
         const FocusRequest& request) {
-    mInputWindowCommands.focusRequests.push_back(request);
+    mInputWindowCommands.addFocusRequest(request);
     return *this;
 }
 
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
         sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
-    mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener);
+    mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
     return *this;
 }
 
@@ -2364,10 +2358,6 @@
     return *this;
 }
 
-bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() {
-    return com::android::graphics::libgui::flags::edge_extension_shader();
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect(
         const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) {
     layer_state_t* s = getLayerState(sc);
@@ -2572,8 +2562,9 @@
     }
     s->what |= layer_state_t::eTrustedPresentationInfoChanged;
     s->trustedPresentationThresholds = thresholds;
-    s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance();
-    s->trustedPresentationListener.callbackId = sc->getLayerId();
+    s->trustedPresentationListener.configure(
+            {.callbackInterface = TransactionCompletedListener::getIInstance(),
+             .callbackId = sc->getLayerId()});
 
     return *this;
 }
@@ -2589,8 +2580,7 @@
     }
     s->what |= layer_state_t::eTrustedPresentationInfoChanged;
     s->trustedPresentationThresholds = TrustedPresentationThresholds();
-    s->trustedPresentationListener.callbackInterface = nullptr;
-    s->trustedPresentationListener.callbackId = -1;
+    s->trustedPresentationListener.clear();
 
     return *this;
 }
@@ -2683,9 +2673,9 @@
         }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, result.handle, result.layerId,
-                                             toString(result.layerName), w, h, format,
-                                             result.transformHint, flags);
+            *outSurface = sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                                   toString(result.layerName), w, h, format,
+                                                   result.transformHint, flags);
         }
     }
     return err;
@@ -2701,7 +2691,8 @@
     const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+        return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                        toString(result.layerName));
     }
     return nullptr;
 }
@@ -2711,7 +2702,8 @@
     const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+        return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                        toString(result.layerName));
     }
     return nullptr;
 }
@@ -2774,6 +2766,7 @@
     if (status.isOk()) {
         // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
         outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
+        outInfo->port = ginfo.port;
         outInfo->density = ginfo.density;
         outInfo->secure = ginfo.secure;
         outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation);
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index b735418..50877f8 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -194,7 +194,7 @@
     return mName;
 }
 
-std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() {
+sp<Choreographer> SurfaceControl::getChoreographer() {
     if (mChoreographer) {
         return mChoreographer;
     }
@@ -203,7 +203,7 @@
         ALOGE("%s: No looper prepared for thread", __func__);
         return nullptr;
     }
-    mChoreographer = std::make_shared<Choreographer>(looper, getHandle());
+    mChoreographer = sp<Choreographer>::make(looper, getHandle());
     status_t result = mChoreographer->initialize();
     if (result != OK) {
         ALOGE("Failed to initialize choreographer");
@@ -269,10 +269,11 @@
     SAFE_PARCEL(parcel.readUint32, &format);
 
     // We aren't the original owner of the surface.
-    *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient(
-                                                    interface_cast<ISurfaceComposerClient>(client)),
-                                            handle.get(), layerId, layerName, width, height, format,
-                                            transformHint);
+    *outSurfaceControl =
+            sp<SurfaceControl>::make(sp<SurfaceComposerClient>::make(
+                                             interface_cast<ISurfaceComposerClient>(client)),
+                                     handle, layerId, layerName, width, height, format,
+                                     transformHint);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index a590c86..14d6df6 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -60,5 +60,34 @@
         }
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "libgui_test",
+      "keywords": [ "primary-device" ],
+      "options": [
+        // TODO(b/397776630): Failing on real devices.
+        {
+          "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow"
+        },
+	// TODO(b/233363648): Failing on real devices.
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+        },
+	// TODO(b/233363648): Flaky on real devices.
+        {
+          "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers"
+        }
+      ]
+    }
   ]
 }
diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp
new file mode 100644
index 0000000..9e09bc2
--- /dev/null
+++ b/libs/gui/TransactionState.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2025 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 "TransactionState"
+#include <gui/LayerState.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/TransactionState.h>
+#include <private/gui/ParcelUtils.h>
+#include <algorithm>
+
+namespace android {
+
+status_t TransactionState::writeToParcel(Parcel* parcel) const {
+    SAFE_PARCEL(parcel->writeUint64, mId);
+    SAFE_PARCEL(parcel->writeUint32, mFlags);
+    SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
+    SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
+    SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
+    SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
+    for (auto const& displayState : mDisplayStates) {
+        displayState.write(*parcel);
+    }
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
+    for (auto const& composerState : mComposerStates) {
+        composerState.write(*parcel);
+    }
+
+    mInputWindowCommands.write(*parcel);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+    }
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+    for (auto mergedTransactionId : mMergedTransactionIds) {
+        SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+    }
+
+    SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
+    for (const auto& [listener, callbackIds] : mListenerCallbacks) {
+        SAFE_PARCEL(parcel->writeStrongBinder, listener);
+        SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionState::readFromParcel(const Parcel* parcel) {
+    SAFE_PARCEL(parcel->readUint64, &mId);
+    SAFE_PARCEL(parcel->readUint32, &mFlags);
+    SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
+    SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
+    SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
+    SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
+
+    uint32_t count;
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mDisplayStates.clear();
+    mDisplayStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        DisplayState displayState;
+        if (displayState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mDisplayStates.emplace_back(std::move(displayState));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mComposerStates.clear();
+    mComposerStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        ComposerState composerState;
+        if (composerState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mComposerStates.emplace_back(std::move(composerState));
+    }
+
+    if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
+        return status;
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mUncacheBuffers.clear();
+    mUncacheBuffers.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        client_cache_t client_cache;
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        client_cache.token = tmpBinder;
+        SAFE_PARCEL(parcel->readUint64, &client_cache.id);
+        mUncacheBuffers.emplace_back(std::move(client_cache));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mMergedTransactionIds.clear();
+    mMergedTransactionIds.resize(count);
+    for (size_t i = 0; i < count; i++) {
+        SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
+    }
+
+    SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
+    mListenerCallbacks.clear();
+    mListenerCallbacks.reserve(count);
+    for (uint32_t i = 0; i < count; i++) {
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        std::vector<CallbackId> callbackIds;
+        SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
+        mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+void TransactionState::merge(TransactionState&& other,
+                             const std::function<void(layer_state_t&)>& onBufferOverwrite) {
+    while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+                   MAX_MERGE_HISTORY_LENGTH - 1 &&
+           mMergedTransactionIds.size() > 0) {
+        mMergedTransactionIds.pop_back();
+    }
+    if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end() - 1);
+    } else if (other.mMergedTransactionIds.size() > 0u) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end());
+    }
+    mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+    for (auto const& otherState : other.mComposerStates) {
+        if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                                   [&otherState](const auto& composerState) {
+                                       return composerState.state.surface ==
+                                               otherState.state.surface;
+                                   });
+            it != mComposerStates.end()) {
+            if (otherState.state.what & layer_state_t::eBufferChanged) {
+                onBufferOverwrite(it->state);
+            }
+            it->state.merge(otherState.state);
+        } else {
+            mComposerStates.push_back(otherState);
+        }
+    }
+
+    for (auto const& state : other.mDisplayStates) {
+        if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                                   [&state](const auto& displayState) {
+                                       return displayState.token == state.token;
+                                   });
+            it != mDisplayStates.end()) {
+            it->merge(state);
+        } else {
+            mDisplayStates.push_back(state);
+        }
+    }
+
+    for (const auto& cacheId : other.mUncacheBuffers) {
+        mUncacheBuffers.push_back(cacheId);
+    }
+
+    mInputWindowCommands.merge(other.mInputWindowCommands);
+    // TODO(b/385156191) Consider merging desired present time.
+    mFlags |= other.mFlags;
+    mMayContainBuffer |= other.mMayContainBuffer;
+    mLogCallPoints |= other.mLogCallPoints;
+
+    // mApplyToken is explicitly not merged. Token should be set before applying the transactions to
+    // make synchronization decisions a bit simpler.
+    mergeFrameTimelineInfo(other.mFrameTimelineInfo);
+    other.clear();
+}
+
+// copied from FrameTimelineInfo::merge()
+void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
+    // When merging vsync Ids we take the oldest valid one
+    if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+        other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+        if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
+            mFrameTimelineInfo = other;
+        }
+    } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+        mFrameTimelineInfo = other;
+    }
+}
+
+void TransactionState::clear() {
+    mComposerStates.clear();
+    mDisplayStates.clear();
+    mListenerCallbacks.clear();
+    mHasListenerCallbacks = false;
+    mInputWindowCommands.clear();
+    mUncacheBuffers.clear();
+    mDesiredPresentTime = 0;
+    mIsAutoTimestamp = true;
+    mApplyToken = nullptr;
+    mFrameTimelineInfo = {};
+    mMergedTransactionIds.clear();
+    mFlags = 0;
+    mMayContainBuffer = false;
+    mLogCallPoints = false;
+}
+
+layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
+    auto handle = sc->getLayerStateHandle();
+    if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                               [&handle](const auto& composerState) {
+                                   return composerState.state.surface == handle;
+                               });
+        it != mComposerStates.end()) {
+        return &it->state;
+    }
+
+    // we don't have it, add an initialized layer_state to our list
+    ComposerState s;
+    s.state.surface = handle;
+    s.state.layerId = sc->getLayerId();
+    mComposerStates.push_back(s);
+
+    return &mComposerStates.back().state;
+}
+
+DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
+    if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                               [token](const auto& display) { return display.token == token; });
+        it != mDisplayStates.end()) {
+        return *it;
+    }
+
+    // If display state doesn't exist, add a new one.
+    DisplayState s;
+    s.token = token;
+    mDisplayStates.push_back(s);
+    return mDisplayStates.back();
+}
+
+}; // namespace android
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 91c9a85..d633f9f 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android/gui/ISurfaceComposer.h>
+#include <android/gui/IWindowInfosListener.h>
 #include <gui/AidlUtil.h>
 #include <gui/WindowInfosListenerReporter.h>
 #include "gui/WindowInfosUpdate.h"
@@ -27,7 +28,7 @@
 using gui::aidl_utils::statusTFromBinderStatus;
 
 sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() {
-    static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter;
+    static sp<WindowInfosListenerReporter> sInstance = sp<WindowInfosListenerReporter>::make();
     return sInstance;
 }
 
@@ -116,7 +117,8 @@
     std::scoped_lock lock(mListenersMutex);
     if (!mWindowInfosListeners.empty()) {
         gui::WindowInfosListenerInfo listenerInfo;
-        composerService->addWindowInfosListener(this, &listenerInfo);
+        composerService->addWindowInfosListener(sp<gui::IWindowInfosListener>::fromExisting(this),
+                                                &listenerInfo);
         mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
         mListenerId = listenerInfo.listenerId;
     }
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index da47ee2..9b2f089 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -67,6 +67,11 @@
         frameRateOverride = 1 << 1,
     }
 
+    enum OptimizationPolicy {
+        optimizeForPower = 0,
+        optimizeForPerformance = 1,
+    }
+
     /**
      * Signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -97,6 +102,10 @@
      *     The name of the virtual display.
      * isSecure
      *     Whether this virtual display is secure.
+     * optimizationPolicy
+     *     Whether to optimize for power or performance. Displays that are optimizing for power may
+     *     be dependent on a different display that optimizes for performance when they are on,
+     *     which will guarantee performance for all of the other displays.
      * uniqueId
      *     The unique ID for the display.
      * requestedRefreshRate
@@ -108,7 +117,7 @@
      * requires ACCESS_SURFACE_FLINGER permission.
      */
     @nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure,
-            @utf8InCpp String uniqueId, float requestedRefreshRate);
+            OptimizationPolicy optimizationPolicy, @utf8InCpp String uniqueId, float requestedRefreshRate);
 
     /**
      * Destroy a virtual display.
diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
index 0ccda56..7ff332c 100644
--- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
@@ -23,6 +23,7 @@
 /** @hide */
 parcelable StaticDisplayInfo {
     DisplayConnectionType connectionType = DisplayConnectionType.Internal;
+    int port = -1;
     float density;
     boolean secure;
     @nullable DeviceProductInfo deviceProductInfo;
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index fd8ffe1..b1a23b3 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -971,7 +971,7 @@
 // H2BGraphicBufferProducer
 
 status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    *buf = new GraphicBuffer();
+    *buf = sp<GraphicBuffer>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->requestBuffer(
             static_cast<int32_t>(slot),
@@ -999,7 +999,7 @@
                                                  uint32_t h, ::android::PixelFormat format,
                                                  uint64_t usage, uint64_t* outBufferAge,
                                                  FrameEventHistoryDelta* outTimestamps) {
-    *fence = new Fence();
+    *fence = sp<Fence>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->dequeueBuffer(
             w, h, static_cast<PixelFormat>(format), uint32_t(usage),
@@ -1035,8 +1035,8 @@
 
 status_t H2BGraphicBufferProducer::detachNextBuffer(
         sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
-    *outBuffer = new GraphicBuffer();
-    *outFence = new Fence();
+    *outBuffer = sp<GraphicBuffer>::make();
+    *outFence = sp<Fence>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->detachNextBuffer(
             [&fnStatus, outBuffer, outFence] (
@@ -1127,8 +1127,8 @@
 status_t H2BGraphicBufferProducer::connect(
         const sp<IProducerListener>& listener, int api,
         bool producerControlledByApp, QueueBufferOutput* output) {
-    sp<HProducerListener> tListener = listener == nullptr ?
-            nullptr : new B2HProducerListener(listener);
+    sp<HProducerListener> tListener =
+            listener == nullptr ? nullptr : sp<B2HProducerListener>::make(listener);
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->connect(
             tListener, static_cast<int32_t>(api), producerControlledByApp,
@@ -1205,13 +1205,13 @@
                     hidl_handle const& fence,
                     hidl_array<float, 16> const& transformMatrix) {
                 fnStatus = toStatusT(status);
-                *outBuffer = new GraphicBuffer();
+                *outBuffer = sp<GraphicBuffer>::make();
                 if (!convertTo(outBuffer->get(), buffer)) {
                     ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
                             "Invalid output buffer");
                     fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
                 }
-                *outFence = new Fence();
+                *outFence = sp<Fence>::make();
                 if (!convertTo(outFence->get(), fence)) {
                     ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
                             "Invalid output fence");
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index c76d771..4384bd5 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -272,7 +272,7 @@
         HConnectionType hConnectionType,
         bool producerControlledByApp,
         connect_cb _hidl_cb) {
-    sp<BProducerListener> bListener = new H2BProducerListener(hListener);
+    sp<BProducerListener> bListener = sp<H2BProducerListener>::make(hListener);
     int bConnectionType{};
     if (!bListener || !h2b(hConnectionType, &bConnectionType)) {
         _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
index ae00a26..7121bb7 100644
--- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -325,7 +325,7 @@
     }
     sp<HProducerListener> hListener = nullptr;
     if (listener && listener->needsReleaseNotify()) {
-        hListener = new B2HProducerListener(listener);
+        hListener = sp<B2HProducerListener>::make(listener);
         if (!hListener) {
             LOG(ERROR) << "connect: failed to wrap listener.";
             return UNKNOWN_ERROR;
diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp
index cbd6cad..c245766 100644
--- a/libs/gui/bufferqueue/2.0/types.cpp
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -147,13 +147,13 @@
 
 bool h2b(native_handle_t const* from, sp<BFence>* to) {
     if (!from || from->numFds == 0) {
-        *to = new ::android::Fence();
+        *to = sp<::android::Fence>::make();
         return true;
     }
     if (from->numFds != 1 || from->numInts != 0) {
         return false;
     }
-    *to = new BFence(dup(from->data[0]));
+    *to = sp<BFence>::make(dup(from->data[0]));
     return true;
 }
 
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index cdc2150..db1b9fb 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -46,21 +46,6 @@
 
 class BLASTBufferItemConsumer : public BufferItemConsumer {
 public:
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-    BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
-                            const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
-          : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
-#else
-    BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
-          : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-            mBLASTBufferQueue(std::move(bbq)),
-            mCurrentlyConnected(false),
-            mPreviouslyConnected(false) {
-    }
-
     void onDisconnect() override EXCLUDES(mMutex);
     void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                   FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex);
@@ -81,6 +66,23 @@
 #endif
 
 private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+                            const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+          : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
+    BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+          : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+            mBLASTBufferQueue(std::move(bbq)),
+            mCurrentlyConnected(false),
+            mPreviouslyConnected(false) {
+    }
+
+    friend class sp<BLASTBufferItemConsumer>;
+
     const wp<BLASTBufferQueue> mBLASTBufferQueue;
 
     uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0;
@@ -94,8 +96,6 @@
 
 class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener {
 public:
-    BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
-
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
         return mProducer;
     }
@@ -158,8 +158,13 @@
     void onFirstRef() override;
 
 private:
+    // Not public to ensure construction via sp<>::make().
+    BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
+
+    friend class sp<BLASTBufferQueue>;
     friend class BLASTBufferQueueHelper;
     friend class BBQBufferQueueProducer;
+    friend class TestBLASTBufferQueue;
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
     friend class BBQBufferQueueCore;
 #endif
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index 6810eda..fc31f46 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -47,6 +47,16 @@
     enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
     enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
 
+    static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create(
+            uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+            bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+
+    static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer,
+                                         uint64_t consumerUsage,
+                                         int bufferCount = DEFAULT_MAX_BUFFERS,
+                                         bool controlledByApp = false)
+            __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
     // Create a new buffer item consumer. The consumerUsage parameter determines
     // the consumer usage flags passed to the graphics allocator. The
     // bufferCount parameter specifies how many buffers can be locked for user
@@ -86,6 +96,14 @@
     status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen,
             bool waitForFence = true);
 
+    // Transfer ownership of a buffer to the BufferQueue. On NO_ERROR, the buffer
+    // is considered as if it were acquired. Buffer must not be null.
+    //
+    // Returns
+    //  - BAD_VALUE if buffer is null
+    //  - INVALID_OPERATION if too many buffers have already been acquired
+    status_t attachBuffer(const sp<GraphicBuffer>& buffer);
+
     // Returns an acquired buffer to the queue, allowing it to be reused. Since
     // only a fixed number of buffers may be acquired at a time, old buffers
     // must be released by calling releaseBuffer to ensure new buffers can be
@@ -95,10 +113,8 @@
     status_t releaseBuffer(const BufferItem &item,
             const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
     status_t releaseBuffer(const sp<GraphicBuffer>& buffer,
                            const sp<Fence>& releaseFence = Fence::NO_FENCE);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 protected:
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index ab1231a..ba6a6a7 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -122,7 +122,10 @@
     // setMaxAcquiredBufferCount sets the maximum number of buffers that can
     // be acquired by the consumer at one time (default 1).  This call will
     // fail if a producer is connected to the BufferQueue.
-    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
+    virtual status_t setMaxAcquiredBufferCount(
+            int maxAcquiredBuffers,
+            std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override;
 
     // setConsumerName sets the name used in logging
     status_t setConsumerName(const String8& name) override;
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index a93ba14..5862967 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -103,7 +103,7 @@
     virtual void handleMessage(const Message& message) override;
 
     static void initJVM(JNIEnv* env);
-    static Choreographer* getForThread();
+    static sp<Choreographer> getForThread();
     static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
     static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 2e347c9..d2215ef 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -123,9 +123,7 @@
     // See IGraphicBufferConsumer::setMaxAcquiredBufferCount
     status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     status_t setConsumerIsProtected(bool isProtected);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
     // See IGraphicBufferConsumer::getSidebandStream
     sp<NativeHandle> getSidebandStream() const;
@@ -141,7 +139,8 @@
     ConsumerBase(const ConsumerBase&);
     void operator=(const ConsumerBase&);
 
-    void initialize(bool controlledByApp);
+    // Requires `this` to be sp/wp so must not be called from ctor.
+    void initialize();
 
 protected:
     // ConsumerBase constructs a new ConsumerBase object to consume image
@@ -192,6 +191,8 @@
 #endif
     virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
 
+    virtual void onBuffersReleasedLocked();
+
     virtual status_t detachBufferLocked(int slotIndex);
 
     // freeBufferLocked frees up the given buffer slot.  If the slot has been
@@ -254,6 +255,10 @@
             const sp<GraphicBuffer> graphicBuffer,
             EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR);
 #endif
+    // Required to complete initialization, so `final` lest overrides forget to
+    // delegate.
+    void onFirstRef() override final;
+
     // returns true iff the slot still has the graphicBuffer in it.
     bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
 
@@ -329,6 +334,8 @@
     // releaseBufferLocked.
     sp<Fence> mPrevFinalReleaseFence;
 
+    const bool mIsControlledByApp;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of ConsumerBase objects. It must be locked whenever the
     // member variables are accessed or when any of the *Locked methods are
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 2bba61b..995cdfb 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -31,6 +31,7 @@
 class BufferQueue;
 class GraphicBuffer;
 class String8;
+class Surface;
 
 /**
  * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
@@ -92,6 +93,13 @@
 
     // Create a new CPU consumer. The maxLockedBuffers parameter specifies
     // how many buffers can be locked for user access at the same time.
+    static std::tuple<sp<CpuConsumer>, sp<Surface>> create(size_t maxLockedBuffers,
+                                                           bool controlledByApp = false,
+                                                           bool isConsumerSurfaceFlinger = false);
+    static sp<CpuConsumer> create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                                  bool controlledByApp = false)
+            __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
                 bool isConsumerSurfaceFlinger = false);
@@ -100,8 +108,8 @@
                 bool controlledByApp = false)
             __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
 #else
-    CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
-            size_t maxLockedBuffers, bool controlledByApp = false);
+    CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                bool controlledByApp = false);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
     // Gets the next graphics buffer from the producer and locks it for CPU use,
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index ab86ac4..187381c 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -18,6 +18,10 @@
 #include <android-base/unique_fd.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+#include <algorithm>
+#include <ostream>
 #include <vector>
 
 namespace android::gui {
@@ -62,4 +66,99 @@
     base::unique_fd fd;
 }; // struct DisplayLuts
 
+static inline void PrintTo(const std::vector<int32_t>& offsets, ::std::ostream* os) {
+    *os << "\n    .offsets = {";
+    for (size_t i = 0; i < offsets.size(); i++) {
+        *os << offsets[i];
+        if (i != offsets.size() - 1) {
+            *os << ", ";
+        }
+    }
+    *os << "}";
+}
+
+static inline void PrintTo(const std::vector<DisplayLuts::Entry>& entries, ::std::ostream* os) {
+    *os << "\n    .lutProperties = {\n";
+    for (auto& [dimension, size, samplingKey] : entries) {
+        *os << "        Entry{"
+            << "dimension: " << dimension << ", size: " << size << ", samplingKey: " << samplingKey
+            << "}\n";
+    }
+    *os << "    }";
+}
+
+static constexpr size_t kMaxPrintCount = 100;
+
+static inline void PrintTo(const std::vector<float>& buffer, size_t offset, int32_t dimension,
+                           size_t size, ::std::ostream* os) {
+    size_t range = std::min(size, kMaxPrintCount);
+    *os << "{";
+    if (dimension == 1) {
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+    } else {
+        *os << "\n        {R channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+        *os << "}\n        {G channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + size + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+        *os << "}\n        {B channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + 2 * size + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+    }
+    *os << "}";
+}
+
+static inline void PrintTo(const std::shared_ptr<DisplayLuts> luts, ::std::ostream* os) {
+    *os << "gui::DisplayLuts {";
+    auto& fd = luts->getLutFileDescriptor();
+    *os << "\n    .pfd = " << fd.get();
+    if (fd.ok()) {
+        PrintTo(luts->offsets, os);
+        PrintTo(luts->lutProperties, os);
+        // decode luts
+        int32_t fullLength = luts->offsets[luts->offsets.size() - 1];
+        if (luts->lutProperties[luts->offsets.size() - 1].dimension == 1) {
+            fullLength += luts->lutProperties[luts->offsets.size() - 1].size;
+        } else {
+            fullLength += (luts->lutProperties[luts->offsets.size() - 1].size *
+                           luts->lutProperties[luts->offsets.size() - 1].size *
+                           luts->lutProperties[luts->offsets.size() - 1].size * 3);
+        }
+        size_t bufferSize = static_cast<size_t>(fullLength) * sizeof(float);
+        float* ptr = (float*)mmap(NULL, bufferSize, PROT_READ, MAP_SHARED, fd.get(), 0);
+        if (ptr == MAP_FAILED) {
+            *os << "\n    .bufferdata cannot mmap!";
+            return;
+        }
+        std::vector<float> buffers(ptr, ptr + fullLength);
+        munmap(ptr, bufferSize);
+
+        *os << "\n    .bufferdata = ";
+        for (size_t i = 0; i < luts->offsets.size(); i++) {
+            PrintTo(buffers, static_cast<size_t>(luts->offsets[i]),
+                    luts->lutProperties[i].dimension,
+                    static_cast<size_t>(luts->lutProperties[i].size), os);
+        }
+    }
+    *os << "\n    }";
+}
+
 } // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index dbf707f..254d8ac 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -83,6 +83,20 @@
     // If the constructor without the tex parameter is used, the GLConsumer is
     // created in a detached state, and attachToContext must be called before
     // calls to updateTexImage.
+    static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t tex, uint32_t textureTarget,
+                                                          bool useFenceSync,
+                                                          bool isControlledByApp);
+    static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t textureTarget, bool useFenceSync,
+                                                          bool isControlledByApp);
+    static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                                 uint32_t textureTarget, bool useFenceSync, bool isControlledByApp)
+            __attribute((deprecated(
+                    "Prefer create functions that create their own surface and consumer.")));
+    static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+                                 bool useFenceSync, bool isControlledByApp)
+            __attribute((deprecated(
+                    "Prefer create functions that create their own surface and consumer.")));
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
 
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 8272a59..8066b07 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -243,6 +243,9 @@
     // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
     // cause the maxBufferCount value to be exceeded.
     //
+    // If called with onBuffersReleasedCallback, that call back will be called in lieu of
+    // IConsumerListener::onBuffersReleased.
+    //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the BufferQueue has been abandoned
     // * BAD_VALUE - one of the below conditions occurred:
@@ -253,6 +256,11 @@
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
 
+    using OnBufferReleasedCallback = std::function<void(void)>;
+    virtual status_t setMaxAcquiredBufferCount(
+            int maxAcquiredBuffers,
+            std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0;
+
     // setConsumerName sets the name used in logging
     virtual status_t setConsumerName(const String8& name) = 0;
 
diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h
index fb4aaa7..b83f245 100644
--- a/libs/gui/include/gui/InputTransferToken.h
+++ b/libs/gui/include/gui/InputTransferToken.h
@@ -39,15 +39,9 @@
         return NO_ERROR;
     };
 
+    bool operator==(const InputTransferToken& other) const { return mToken == other.mToken; }
+
     sp<IBinder> mToken;
 };
 
-static inline bool operator==(const sp<InputTransferToken>& token1,
-                              const sp<InputTransferToken>& token2) {
-    if (token1.get() == token2.get()) {
-        return true;
-    }
-    return token1->mToken == token2->mToken;
-}
-
 } // namespace android
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 7ee291d..6381db2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -44,6 +44,10 @@
     LayerMetadata& operator=(const LayerMetadata& other);
     LayerMetadata& operator=(LayerMetadata&& other);
 
+    // Note: `default` is not feasible because Parcelable does not provide ==.
+    bool operator==(const LayerMetadata& rhs) const { return mMap == rhs.mMap; }
+    bool operator!=(const LayerMetadata&) const = default;
+
     // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in
     // in this whose keys are paired with empty values in other will be erased.
     bool merge(const LayerMetadata& other, bool eraseEmpty = false);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index c2680a4..369d3d1 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -17,9 +17,9 @@
 #ifndef ANDROID_SF_LAYER_STATE_H
 #define ANDROID_SF_LAYER_STATE_H
 
-
 #include <stdint.h>
 #include <sys/types.h>
+#include <span>
 
 #include <android/gui/DisplayCaptureArgs.h>
 #include <android/gui/IWindowInfosReportedListener.h>
@@ -69,21 +69,39 @@
     uint64_t id;
 
     bool operator==(const client_cache_t& other) const { return id == other.id; }
+    bool operator!=(const client_cache_t&) const = default;
 
     bool isValid() const { return token != nullptr; }
 };
 
 class TrustedPresentationListener : public Parcelable {
 public:
-    sp<ITransactionCompletedListener> callbackInterface;
-    int callbackId = -1;
+    struct State {
+        sp<ITransactionCompletedListener> callbackInterface;
+        int callbackId = -1;
+        bool operator==(const State&) const = default;
+        bool operator!=(const State&) const = default;
+    };
 
     void invoke(bool presentedWithinThresholds) {
-        callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds);
+        mState.callbackInterface->onTrustedPresentationChanged(mState.callbackId,
+                                                               presentedWithinThresholds);
+    }
+    void configure(State&& state) { mState = std::move(state); }
+    const sp<ITransactionCompletedListener>& getCallback() { return mState.callbackInterface; }
+    void clear() {
+        mState.callbackInterface = nullptr;
+        mState.callbackId = -1;
     }
 
     status_t writeToParcel(Parcel* parcel) const;
     status_t readFromParcel(const Parcel* parcel);
+
+    bool operator==(const TrustedPresentationListener& rhs) const { return mState == rhs.mState; }
+    bool operator!=(const TrustedPresentationListener&) const = default;
+
+private:
+    State mState;
 };
 
 class BufferData : public Parcelable {
@@ -309,6 +327,32 @@
     bool hasValidBuffer() const;
     void sanitize(int32_t permissions);
 
+    void updateTransparentRegion(const Region& transparentRegion);
+    const Region& getTransparentRegion() const { return mNotDefCmpState.transparentRegion; }
+    void updateSurfaceDamageRegion(const Region& surfaceDamageRegion);
+    const Region& getSurfaceDamageRegion() const { return mNotDefCmpState.surfaceDamageRegion; }
+    // Do not update state flags.  Used to set up test state.
+    void setSurfaceDamageRegion(Region&& surfaceDamageRegion) {
+        mNotDefCmpState.surfaceDamageRegion = std::move(surfaceDamageRegion);
+    }
+    void updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z);
+    void updateParentLayer(const sp<SurfaceControl>& newParent);
+    void updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info);
+    const gui::WindowInfo& getWindowInfo() const {
+        return *mNotDefCmpState.windowInfoHandle->getInfo();
+    }
+    gui::WindowInfo* editWindowInfo() { return mNotDefCmpState.windowInfoHandle->editInfo(); }
+
+    const sp<SurfaceControl>& getParentSurfaceControlForChild() const {
+        return mNotDefCmpState.parentSurfaceControlForChild;
+    }
+    const sp<SurfaceControl>& getRelativeLayerSurfaceControl() const {
+        return mNotDefCmpState.relativeLayerSurfaceControl;
+    }
+
+    bool operator==(const layer_state_t&) const = default;
+    bool operator!=(const layer_state_t&) const = default;
+
     struct matrix22_t {
         float dsdx{0};
         float dtdx{0};
@@ -337,28 +381,20 @@
     float clientDrawnCornerRadius;
     uint32_t backgroundBlurRadius;
 
-    sp<SurfaceControl> relativeLayerSurfaceControl;
-
-    sp<SurfaceControl> parentSurfaceControlForChild;
-
     half4 color;
 
     // non POD must be last. see write/read
-    Region transparentRegion;
     uint32_t bufferTransform;
     bool transformToDisplayInverse;
     FloatRect crop;
     std::shared_ptr<BufferData> bufferData = nullptr;
     ui::Dataspace dataspace;
     HdrMetadata hdrMetadata;
-    Region surfaceDamageRegion;
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
     std::vector<BlurRegion> blurRegions;
 
-    sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
-
     LayerMetadata metadata;
 
     // The following refer to the alpha, and dataspace, respectively of
@@ -444,6 +480,18 @@
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
 
     std::shared_ptr<gui::DisplayLuts> luts;
+
+protected:
+    struct NotDefaultComparableState {
+        Region transparentRegion;
+        Region surfaceDamageRegion;
+        sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+        sp<SurfaceControl> relativeLayerSurfaceControl;
+        sp<SurfaceControl> parentSurfaceControlForChild;
+
+        bool operator==(const NotDefaultComparableState& rhs) const;
+        bool operator!=(const NotDefaultComparableState& rhs) const = default;
+    } mNotDefCmpState;
 };
 
 class ComposerState {
@@ -451,6 +499,9 @@
     layer_state_t state;
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    bool operator==(const ComposerState&) const = default;
+    bool operator!=(const ComposerState&) const = default;
 };
 
 struct DisplayState {
@@ -516,20 +567,35 @@
 
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    bool operator==(const DisplayState&) const = default;
+    bool operator!=(const DisplayState&) const = default;
 };
 
 struct InputWindowCommands {
-    std::vector<gui::FocusRequest> focusRequests;
-    std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                       SpHash<gui::IWindowInfosReportedListener>>
-            windowInfosReportedListeners;
-
+    using Listener = gui::IWindowInfosReportedListener;
+    using ListenerSet = std::unordered_set<sp<Listener>, SpHash<Listener>>;
     // Merges the passed in commands and returns true if there were any changes.
     bool merge(const InputWindowCommands& other);
     bool empty() const;
     void clear();
+    void addFocusRequest(const gui::FocusRequest& request) { focusRequests.push_back(request); }
+    void addWindowInfosReportedListener(const sp<Listener>& listener) {
+        windowInfosReportedListeners.insert(listener);
+    }
+    ListenerSet&& releaseListeners() { return std::move(windowInfosReportedListeners); }
+
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    std::span<const gui::FocusRequest> getFocusRequests() const { return focusRequests; }
+    const ListenerSet& getListeners() const { return windowInfosReportedListeners; }
+    bool operator==(const InputWindowCommands&) const = default;
+    bool operator!=(const InputWindowCommands&) const = default;
+
+private:
+    std::vector<gui::FocusRequest> focusRequests;
+    ListenerSet windowInfosReportedListeners;
 };
 
 static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h
index 28237b6..8176f75 100644
--- a/libs/gui/include/gui/StreamSplitter.h
+++ b/libs/gui/include/gui/StreamSplitter.h
@@ -153,6 +153,8 @@
         size_t mReleaseCount;
     };
 
+    friend class sp<StreamSplitter>;
+
     // Only called from createSplitter
     explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue);
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 10225cc..4fda8de 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -345,8 +345,6 @@
     static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>
     getDisplayDecorationSupport(const sp<IBinder>& displayToken);
 
-    static bool flagEdgeExtensionEffectUseShader();
-
     /**
      * Returns how many picture profiles are supported by the display.
      *
@@ -396,6 +394,7 @@
 
     static const std::string kEmpty;
     static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+                                            bool optimizeForPower = true,
                                             const std::string& uniqueId = kEmpty,
                                             float requestedRefreshRate = 0);
 
@@ -620,7 +619,7 @@
         Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
                                                 float currentBufferRatio, float desiredRatio);
         Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio);
-        Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+        Transaction& setLuts(const sp<SurfaceControl>& sc, base::unique_fd&& lutFd,
                              const std::vector<int32_t>& offsets,
                              const std::vector<int32_t>& dimensions,
                              const std::vector<int32_t>& sizes,
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 344b957..91a422d 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -26,6 +26,7 @@
 
 #include <android/gui/ISurfaceComposerClient.h>
 
+#include <gui/Choreographer.h>
 #include <ui/FrameStats.h>
 #include <ui/PixelFormat.h>
 #include <ui/Region.h>
@@ -36,7 +37,6 @@
 
 // ---------------------------------------------------------------------------
 
-class Choreographer;
 class IGraphicBufferProducer;
 class Surface;
 class SurfaceComposerClient;
@@ -82,7 +82,7 @@
     const std::string& getName() const;
 
     // TODO(b/267195698): Consider renaming.
-    std::shared_ptr<Choreographer> getChoreographer();
+    sp<Choreographer> getChoreographer();
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer();
 
@@ -134,7 +134,7 @@
     PixelFormat mFormat = PIXEL_FORMAT_NONE;
     uint32_t mCreateFlags = 0;
     uint64_t mFallbackFrameNumber = 100;
-    std::shared_ptr<Choreographer> mChoreographer;
+    sp<Choreographer> mChoreographer;
 };
 
 }; // namespace android
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
new file mode 100644
index 0000000..4358227
--- /dev/null
+++ b/libs/gui/include/gui/TransactionState.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/FrameTimelineInfo.h>
+#include <binder/Parcelable.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+// Class to store all the transaction data and the parcelling logic
+class TransactionState {
+public:
+    explicit TransactionState() = default;
+    TransactionState(TransactionState const& other) = default;
+    status_t writeToParcel(Parcel* parcel) const;
+    status_t readFromParcel(const Parcel* parcel);
+    layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+    DisplayState& getDisplayState(const sp<IBinder>& token);
+
+    // Returns the current id of the transaction.
+    // The id is updated every time the transaction is applied.
+    uint64_t getId() const { return mId; }
+    std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
+    void enableDebugLogCallPoints() { mLogCallPoints = true; }
+    void merge(TransactionState&& other,
+               const std::function<void(layer_state_t&)>& onBufferOverwrite);
+
+    // copied from FrameTimelineInfo::merge()
+    void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
+    void clear();
+    bool operator==(const TransactionState& rhs) const = default;
+    bool operator!=(const TransactionState& rhs) const = default;
+
+    uint64_t mId = 0;
+    std::vector<uint64_t> mMergedTransactionIds;
+    uint32_t mFlags = 0;
+    // The vsync id provided by Choreographer.getVsyncId and the input event id
+    gui::FrameTimelineInfo mFrameTimelineInfo;
+    // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+    // to be presented. When it is not possible to present at exactly that time, it will be
+    // presented after the time has passed.
+    //
+    // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+    // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+    //
+    // Desired present times that are more than 1 second in the future may be ignored.
+    // When a desired present time has already passed, the transaction will be presented as soon
+    // as possible.
+    //
+    // Transactions from the same process are presented in the same order that they are applied.
+    // The desired present time does not affect this ordering.
+    int64_t mDesiredPresentTime = 0;
+    bool mIsAutoTimestamp = true;
+    // If not null, transactions will be queued up using this token otherwise a common token
+    // per process will be used.
+    sp<IBinder> mApplyToken;
+    // Indicates that the Transaction may contain buffers that should be cached. The reason this
+    // is only a guess is that buffers can be removed before cache is called. This is only a
+    // hint that at some point a buffer was added to this transaction before apply was called.
+    bool mMayContainBuffer = false;
+    // Prints debug logs when enabled.
+    bool mLogCallPoints = false;
+
+    std::vector<DisplayState> mDisplayStates;
+    std::vector<ComposerState> mComposerStates;
+    InputWindowCommands mInputWindowCommands;
+    std::vector<client_cache_t> mUncacheBuffers;
+    // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
+    // empty.
+    bool mHasListenerCallbacks = false;
+    std::vector<ListenerCallbacks> mListenerCallbacks;
+
+private:
+    // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+    // Ordered most recently merged to least recently merged.
+    static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+};
+
+}; // namespace android
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 420dc21..9ac49c0 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -268,6 +268,7 @@
     bool overlaps(const WindowInfo* other) const;
 
     bool operator==(const WindowInfo& inputChannel) const;
+    bool operator!=(const WindowInfo&) const = default;
 
     status_t writeToParcel(android::Parcel* parcel) const override;
 
@@ -319,6 +320,9 @@
     status_t readFromParcel(const android::Parcel* parcel);
     status_t writeToParcel(android::Parcel* parcel) const;
 
+    bool operator==(const WindowInfoHandle& rhs) const { return mInfo == rhs.mInfo; }
+    bool operator!=(const WindowInfoHandle&) const = default;
+
 protected:
     virtual ~WindowInfoHandle();
 
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
index 24d26b1..18a7e12 100644
--- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -47,6 +47,7 @@
     MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
     MOCK_METHOD1(setMaxBufferCount, status_t(int));
     MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
+    MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>));
     MOCK_METHOD1(setConsumerName, status_t(const String8&));
     MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
     MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 90d91ac..ce1bc95 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -77,14 +77,6 @@
 } # wb_stream_splitter
 
 flag {
-  name: "edge_extension_shader"
-  namespace: "windowing_frontend"
-  description: "Enable edge extension via shader"
-  bug: "322036393"
-  is_fixed_read_only: true
-} # edge_extension_shader
-
-flag {
   name: "buffer_release_channel"
   namespace: "window_surfaces"
   description: "Enable BufferReleaseChannel to optimize buffer releases"
@@ -138,9 +130,6 @@
   description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks"
   bug: "339705065"
   is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
 } # bq_gl_fence_cleanup
 
 flag {
@@ -150,3 +139,25 @@
   bug: "340934031"
   is_fixed_read_only: true
 } # wb_media_migration
+
+flag {
+  name: "allocate_buffer_priority"
+  namespace: "wear_system_health"
+  description: "Boost priority for buffer allocation"
+  bug: "399701430"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+} # allocate_buffer_priority
+
+flag {
+  name: "bq_always_use_max_dequeued_buffer_count"
+  namespace: "core_graphics"
+  description: "BufferQueueProducer::dequeue's respects setMaxDequeuedBufferCount even before a buffer is dequeued."
+  bug: "399328309"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+} # bq_always_use_max_dequeued_buffer_count
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 87051a7..bd53031 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -51,11 +51,6 @@
         "-Werror",
         "-Wextra",
         "-Wthread-safety",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true",
     ],
 
     srcs: [
@@ -90,6 +85,7 @@
         "testserver/TestServerClient.cpp",
         "testserver/TestServerHost.cpp",
         "TextureRenderer.cpp",
+        "TransactionState_test.cpp",
         "VsyncEventData_test.cpp",
         "WindowInfo_test.cpp",
     ],
@@ -99,6 +95,7 @@
         "android.hardware.configstore-utils",
         "libSurfaceFlingerProp",
         "libGLESv1_CM",
+        "libgui",
         "libgui_test_server_aidl-cpp",
         "libinput",
         "libnativedisplay",
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index e6ee89f..4e4c8a2 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -114,8 +114,8 @@
 class BLASTBufferQueueHelper {
 public:
     BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
-        mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
-                                                            height, PIXEL_FORMAT_RGBA_8888);
+        mBlastBufferQueueAdapter = sp<TestBLASTBufferQueue>::make("TestBLASTBufferQueue", sc, width,
+                                                                  height, PIXEL_FORMAT_RGBA_8888);
     }
 
     void update(const sp<SurfaceControl>& sc, int width, int height) {
@@ -201,7 +201,7 @@
 protected:
     void SetUp() {
         mComposer = ComposerService::getComposerService();
-        mClient = new SurfaceComposerClient();
+        mClient = sp<SurfaceComposerClient>::make();
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
         // display 0 is picked as this test is not much display depedent
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 6453885..80eea26 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -24,6 +24,7 @@
 #include <gui/Surface.h>
 #include <ui/BufferQueueDefs.h>
 #include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
 
 #include <unordered_set>
 
@@ -62,14 +63,15 @@
     void SetUp() override {
         mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS);
 
-        mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
+        sp<Surface> surface;
+        std::tie(mBIC, surface) = BufferItemConsumer::create(kUsage, kMaxLockedBuffers, true);
         String8 name("BufferItemConsumer_Under_Test");
         mBIC->setName(name);
         mBFL = new BufferFreedListener(this);
         mBIC->setBufferFreedListener(mBFL);
 
         sp<IProducerListener> producerListener = new TrackingProducerListener(this);
-        mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
+        mProducer = surface->getIGraphicBufferProducer();
         IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         ASSERT_EQ(NO_ERROR,
                   mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -234,6 +236,38 @@
     ASSERT_EQ(1, GetFreedBufferCount());
 }
 
+TEST_F(BufferItemConsumerTest, ResizeAcquireCount) {
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+}
+
+TEST_F(BufferItemConsumerTest, AttachBuffer) {
+    ASSERT_EQ(OK, mBIC->setMaxAcquiredBufferCount(1));
+
+    int slot;
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+    AcquireBuffer(&slot);
+
+    sp<GraphicBuffer> newBuffer1 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+    sp<GraphicBuffer> newBuffer2 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+
+    // For some reason, you can attach an extra buffer?
+    // b/400973991 to investigate
+    EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer1));
+    EXPECT_EQ(INVALID_OPERATION, mBIC->attachBuffer(newBuffer2));
+
+    ReleaseBuffer(slot);
+
+    EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer2));
+    EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer1, Fence::NO_FENCE));
+    EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer2, Fence::NO_FENCE));
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 // Test that delete BufferItemConsumer triggers onBufferFreed.
 TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index cfbb2e7..e22f57e 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1452,10 +1452,6 @@
     ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
 }
 
-TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) {
-    ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE));
-}
-
 struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
     BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
 
@@ -1561,9 +1557,14 @@
             {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
     }};
 
-    ASSERT_EQ(NO_INIT,
-              native_window_set_buffers_additional_options(surface.get(), extras.data(),
-                                                           extras.size()));
+    auto status = native_window_set_buffers_additional_options(surface.get(), extras.data(),
+                                                               extras.size());
+    if (flags::bq_extendedallocate()) {
+        ASSERT_EQ(NO_INIT, status);
+    } else {
+        ASSERT_EQ(INVALID_OPERATION, status);
+        GTEST_SKIP() << "Flag bq_extendedallocate not enabled";
+    }
 
     if (!IsCuttlefish()) {
         GTEST_SKIP() << "Not cuttlefish";
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 8db48d2..314dea6 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -50,7 +50,7 @@
 
 TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
     sp<Looper> looper = Looper::prepare(0);
-    Choreographer* choreographer = Choreographer::getForThread();
+    sp<Choreographer> choreographer = Choreographer::getForThread();
     VsyncCallback animationCb;
     choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
                                             CALLBACK_ANIMATION);
@@ -83,4 +83,4 @@
                                            animationCb.frameTime.count());
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 9476930..482cfde 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,10 +66,9 @@
                 test_info->name(),
                 params.width, params.height,
                 params.maxLockedBuffers, params.format);
-        mCC = new CpuConsumer(params.maxLockedBuffers);
+        std::tie(mCC, mSTC) = CpuConsumer::create(params.maxLockedBuffers);
         String8 name("CpuConsumer_Under_Test");
         mCC->setName(name);
-        mSTC = mCC->getSurface();
         mANW = mSTC;
     }
 
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index bffb3f0..62d73ca 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -30,7 +30,7 @@
 class DisplayedContentSamplingTest : public ::testing::Test {
 protected:
     void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(OK, mComposerClient->initCheck());
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index cf05fd4..5a5067b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -222,8 +222,8 @@
         ASSERT_EQ(InputEventType::MOTION, ev->getType());
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
-        EXPECT_EQ(x, mev->getX(0));
-        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_NEAR(x, mev->getX(0), EPSILON);
+        EXPECT_NEAR(y, mev->getY(0), EPSILON);
         EXPECT_EQ(flags, mev->getFlags() & flags);
 
         ev = consumeEvent();
@@ -241,8 +241,8 @@
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
         const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
-        EXPECT_EQ(displayX, coords.getX());
-        EXPECT_EQ(displayY, coords.getY());
+        EXPECT_NEAR(displayX, coords.getX(), EPSILON);
+        EXPECT_NEAR(displayY, coords.getY(), EPSILON);
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
@@ -398,7 +398,7 @@
     InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
 
     void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index 40af8e8..407c18e 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -56,7 +56,7 @@
     }
 
     if (mDisplaySecs > 0) {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         mSurfaceControl = mComposerClient->createSurface(
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 2428bb3..7ae6f40 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,8 +34,8 @@
 
     virtual void SetUp() {
         GLTest::SetUp();
-        mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSurface = mGlConsumer->getSurface();
+        std::tie(mGlConsumer, mSurface) =
+                GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSurface.get();
 
     }
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index c35efe2..d80d223 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -180,7 +180,7 @@
     }
 
     void SetUp() override {
-        mSurfaceComposerClient = new SurfaceComposerClient;
+        mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
 
         mBackgroundLayer =
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 8fea689..a2fe8fd 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -36,7 +36,7 @@
 class Button : public gui::BnRegionSamplingListener {
 public:
     Button(const char* name, const Rect& samplingArea) {
-        sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+        sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
 
         mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
                                         ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 59d05b6..d3434ea 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,8 +40,7 @@
     }
 
     virtual void SetUp() {
-        mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSTC = mST->getSurface();
+        std::tie(mST, mSTC) = GLConsumer::create(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSTC;
 
         // We need a valid GL context so we can test updateTexImage()
@@ -727,8 +726,7 @@
         ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
 
         for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
-            sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
-            sp<Surface> stc = st->getSurface();
+            auto [st, stc] = GLConsumer::create(i, GLConsumer::TEXTURE_EXTERNAL, true, false);
             mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
                     static_cast<ANativeWindow*>(stc.get()), nullptr);
             ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 1309635..eb31cd1 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,8 +38,7 @@
 
     void SetUp() {
         GLTest::SetUp();
-        mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSTC = mST->getSurface();
+        std::tie(mST, mSTC) = GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSTC;
         ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
         mTextureRenderer = new TextureRenderer(TEX_ID, mST);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 98d1329..61c93ca 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -134,7 +134,7 @@
     }
 
     virtual void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         // TODO(brianderson): The following sometimes fails and is a source of
@@ -294,9 +294,7 @@
 TEST_F(SurfaceTest, QueryConsumerUsage) {
     const int TEST_USAGE_FLAGS =
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
-    sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
-
-    sp<Surface> s = c->getSurface();
+    auto [c, s] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
     sp<ANativeWindow> anw(s);
 
     int flags = -1;
@@ -309,10 +307,8 @@
 TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
     const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+    auto [cpuConsumer, s] = CpuConsumer::create(1);
     cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
-
-    sp<Surface> s = cpuConsumer->getSurface();
     sp<ANativeWindow> anw(s);
 
     android_dataspace dataSpace;
@@ -325,8 +321,7 @@
 }
 
 TEST_F(SurfaceTest, SettingGenerationNumber) {
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
 
     // Allocate a buffer with a generation number of 0
@@ -692,10 +687,11 @@
         return binder::Status::ok();
     }
 
-    binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/,
-                                        const std::string& /*uniqueId*/,
-                                        float /*requestedRefreshRate*/,
-                                        sp<IBinder>* /*outDisplay*/) override {
+    binder::Status createVirtualDisplay(
+            const std::string& /*displayName*/, bool /*isSecure*/,
+            gui::ISurfaceComposer::OptimizationPolicy /*optimizationPolicy*/,
+            const std::string& /*uniqueId*/, float /*requestedRefreshRate*/,
+            sp<IBinder>* /*outDisplay*/) override {
         return binder::Status::ok();
     }
 
@@ -2180,8 +2176,7 @@
     const int BUFFER_COUNT = 16;
     const int BATCH_SIZE = 8;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
     sp<StubSurfaceListener> listener = new StubSurfaceListener();
 
@@ -2229,8 +2224,7 @@
     const int BUFFER_COUNT = 16;
     const int BATCH_SIZE = 8;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
     sp<StubSurfaceListener> listener = new StubSurfaceListener();
 
@@ -2252,6 +2246,52 @@
     ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
 }
 
+TEST_F(SurfaceTest, setMaxDequeuedBufferCount_setMaxAcquiredBufferCount_allocations) {
+    //
+    // Set up the consumer and producer--nothing fancy.
+    //
+    auto [consumer, surface] =
+            BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener);
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+
+    //
+    // These values are independent. The consumer can dequeue 3 and the consumer can acquire 3 at
+    // the same time.
+    //
+    ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(3));
+    ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(3));
+
+    //
+    // Take all three buffers out of the queue--a fourth can't be retrieved. Then queue them.
+    //
+    std::vector<Surface::BatchBuffer> dequeuedBuffers(3);
+    EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+    if (::com::android::graphics::libgui::flags::bq_always_use_max_dequeued_buffer_count()) {
+        EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+    }
+
+    for (auto& batchBuffer : dequeuedBuffers) {
+        EXPECT_EQ(OK,
+                  surface->queueBuffer(GraphicBuffer::from(batchBuffer.buffer),
+                                       sp<Fence>::make(batchBuffer.fenceFd)));
+    }
+    dequeuedBuffers.assign(3, {});
+
+    //
+    // Acquire all three, then we should be able to dequeue 3 more.
+    //
+    std::vector<BufferItem> acquiredBuffers(3);
+    for (auto& bufferItem : acquiredBuffers) {
+        EXPECT_EQ(OK, consumer->acquireBuffer(&bufferItem, 0));
+    }
+
+    EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+    EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 TEST_F(SurfaceTest, PlatformBufferMethods) {
@@ -2377,8 +2417,7 @@
     sp<IGraphicBufferConsumer> bqConsumer;
     BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
 
-    sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3);
-    sp<Surface> surface = sp<Surface>::make(bqProducer);
+    auto [consumer, surface] = BufferItemConsumer::create(3);
     sp<ImmediateReleaseConsumerListener> consumerListener =
             sp<ImmediateReleaseConsumerListener>::make(consumer);
     consumer->setFrameAvailableListener(consumerListener);
diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp
new file mode 100644
index 0000000..179b264
--- /dev/null
+++ b/libs/gui/tests/TransactionState_test.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 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 <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+#include "android/gui/FocusRequest.h"
+#include "binder/Binder.h"
+#include "binder/Parcel.h"
+#include "gtest/gtest.h"
+#include "gui/LayerState.h"
+#include "gui/WindowInfo.h"
+
+#include "gui/TransactionState.h"
+
+namespace android {
+
+void sprintf(std::string& out, const char* format, ...) {
+    va_list arg_list;
+    va_start(arg_list, format);
+
+    int len = vsnprintf(nullptr, 0, format, arg_list);
+    if (len < 0) {
+        va_end(arg_list);
+    }
+    std::string line(len, '\0');
+    int written = vsnprintf(line.data(), len + 1, format, arg_list);
+    if (written != len) {
+        va_end(arg_list);
+    }
+    line.pop_back();
+    out += line;
+    va_end(arg_list);
+}
+
+constexpr std::string dump_struct(auto& x) {
+    std::string s;
+#if __has_builtin(__builtin_dump_struct)
+    __builtin_dump_struct(&x, sprintf, s);
+#else
+    (void)x;
+#endif
+    return s;
+}
+
+void PrintTo(const TransactionState& state, ::std::ostream* os) {
+    *os << dump_struct(state);
+    *os << state.mFrameTimelineInfo.toString();
+    for (auto mergedId : state.mMergedTransactionIds) {
+        *os << mergedId << ",";
+    }
+}
+
+void PrintTo(const ComposerState& state, ::std::ostream* os) {
+    *os << dump_struct(state.state);
+    *os << state.state.getWindowInfo();
+}
+
+// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
+// field did not compare ==.
+void Compare(const TransactionState& s1, const TransactionState& s2) {
+    EXPECT_EQ(s1.mId, s2.mId);
+    EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
+    EXPECT_EQ(s1.mFlags, s2.mFlags);
+    EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
+    EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
+    EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
+    EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
+    EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
+    EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
+    EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
+    EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
+    EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
+    EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
+    EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
+    EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
+    EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
+    EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
+    EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
+}
+
+std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
+    auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
+    for (size_t i = 0; i < maxSize; ++i) {
+        result->emplace(i, sp<BBinder>::make());
+    }
+    return result;
+}
+
+constexpr size_t kMaxComposerStates = 2;
+ComposerState createComposerStateForTest(size_t i) {
+    static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
+
+    ComposerState state;
+    state.state.what = layer_state_t::eFlagsChanged;
+    state.state.surface = sLayerHandle->at(i);
+    state.state.layerId = i;
+    state.state.flags = 20 * i;
+    return state;
+}
+
+constexpr size_t kMaxDisplayStates = 5;
+DisplayState createDisplayStateForTest(size_t i) {
+    static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
+
+    DisplayState displayState;
+    displayState.what = DisplayState::eFlagsChanged;
+    displayState.token = sDisplayTokens->at(i);
+    displayState.flags = 20 * i;
+    return displayState;
+}
+
+TransactionState createTransactionStateForTest() {
+    static sp<BBinder> sApplyToken = sp<BBinder>::make();
+
+    TransactionState state;
+    state.mId = 123;
+    state.mMergedTransactionIds.push_back(15);
+    state.mMergedTransactionIds.push_back(0);
+    state.mFrameTimelineInfo.vsyncId = 14;
+    state.mDesiredPresentTime = 11;
+    state.mIsAutoTimestamp = true;
+    state.mApplyToken = sApplyToken;
+    for (size_t i = 0; i < kMaxDisplayStates; i++) {
+        state.mDisplayStates.push_back(createDisplayStateForTest(i));
+    }
+    for (size_t i = 0; i < kMaxComposerStates; i++) {
+        state.mComposerStates.push_back(createComposerStateForTest(i));
+    }
+    static const auto* const sFocusRequestTokens = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        gui::FocusRequest request;
+        request.token = sFocusRequestTokens->at(i);
+        request.timestamp = i;
+        state.mInputWindowCommands.addFocusRequest(request);
+    }
+    static const auto* const sCacheToken = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        client_cache_t cache;
+        cache.token = sCacheToken->at(i);
+        cache.id = i;
+        state.mUncacheBuffers.emplace_back(std::move(cache));
+    }
+    static const auto* const sListenerCallbacks = []() {
+        auto* callbacks = new std::vector<ListenerCallbacks>();
+        for (int i = 0; i < 5; i++) {
+            callbacks->emplace_back(sp<BBinder>::make(),
+                                    std::unordered_set<CallbackId, CallbackIdHash>{});
+        }
+        return callbacks;
+    }();
+    state.mHasListenerCallbacks = true;
+    state.mListenerCallbacks = *sListenerCallbacks;
+    return state;
+}
+
+TransactionState createEmptyTransaction(uint64_t id) {
+    TransactionState state;
+    state.mId = id;
+    return state;
+}
+
+TEST(TransactionStateTest, parcel) {
+    TransactionState state = createTransactionStateForTest();
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    parcelledState.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelDisplayState) {
+    DisplayState state = createDisplayStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    DisplayState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelLayerState) {
+    ComposerState state = createComposerStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    ComposerState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelEmptyState) {
+    TransactionState state;
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    state.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, mergeLayerState) {
+    ComposerState composerState = createComposerStateForTest(0);
+    ComposerState update;
+    update.state.surface = composerState.state.surface;
+    update.state.layerId = 0;
+    update.state.what = layer_state_t::eAlphaChanged;
+    update.state.color.a = .42;
+    composerState.state.merge(update.state);
+
+    ComposerState expectedMergedState = createComposerStateForTest(0);
+    expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.state.color.a = .42;
+    EXPECT_EQ(composerState, expectedMergedState);
+};
+
+TEST(TransactionStateTest, merge) {
+    // Setup.
+    static constexpr uint64_t kUpdateTransactionId = 200;
+
+    TransactionState state = createTransactionStateForTest();
+
+    TransactionState update;
+    update.mId = kUpdateTransactionId;
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[0].state.surface;
+        composerState.state.what = layer_state_t::eAlphaChanged;
+        composerState.state.color.a = .42;
+        update.mComposerStates.push_back(composerState);
+    }
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[1].state.surface;
+        composerState.state.what = layer_state_t::eBufferChanged;
+        update.mComposerStates.push_back(composerState);
+    }
+    int32_t overrwiteLayerId = -1;
+    // Mutation.
+    state.merge(std::move(update),
+                [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
+    // Assertions.
+    EXPECT_EQ(1, overrwiteLayerId);
+    EXPECT_EQ(update, createEmptyTransaction(update.getId()));
+
+    TransactionState expectedMergedState = createTransactionStateForTest();
+    expectedMergedState.mMergedTransactionIds
+            .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
+    expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.mComposerStates.at(0).state.color.a = .42;
+    expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
+    auto inputCommands = expectedMergedState.mInputWindowCommands;
+
+    // desired present time is not merged.
+    expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
+
+    EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
+    EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
+    EXPECT_EQ(state, expectedMergedState);
+};
+
+TEST(TransactionStateTest, clear) {
+    TransactionState state = createTransactionStateForTest();
+    state.clear();
+    TransactionState emptyState = createEmptyTransaction(state.getId());
+    EXPECT_EQ(state, emptyState);
+};
+
+} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index d2e4320..52e0276 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -225,11 +225,13 @@
     srcs: [
         "AccelerationCurve.cpp",
         "CoordinateFilter.cpp",
+        "DisplayTopologyGraph.cpp",
         "Input.cpp",
         "InputConsumer.cpp",
         "InputConsumerNoResampling.cpp",
         "InputDevice.cpp",
         "InputEventLabels.cpp",
+        "InputFlags.cpp",
         "InputTransport.cpp",
         "InputVerifier.cpp",
         "KeyCharacterMap.cpp",
@@ -269,6 +271,7 @@
 
     shared_libs: [
         "android.companion.virtualdevice.flags-aconfig-cc",
+        "com.android.window.flags.window-aconfig_flags_c_lib",
         "libPlatformProperties",
         "libaconfig_storage_read_api_cc",
         "libbase",
diff --git a/libs/input/DisplayTopologyGraph.cpp b/libs/input/DisplayTopologyGraph.cpp
new file mode 100644
index 0000000..934f2e8
--- /dev/null
+++ b/libs/input/DisplayTopologyGraph.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 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 "DisplayTopologyValidator"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <input/DisplayTopologyGraph.h>
+#include <input/PrintTools.h>
+#include <ui/LogicalDisplayId.h>
+
+#include <algorithm>
+
+#define INDENT "  "
+
+namespace android {
+
+namespace {
+
+DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) {
+    switch (position) {
+        case DisplayTopologyPosition::LEFT:
+            return DisplayTopologyPosition::RIGHT;
+        case DisplayTopologyPosition::TOP:
+            return DisplayTopologyPosition::BOTTOM;
+        case DisplayTopologyPosition::RIGHT:
+            return DisplayTopologyPosition::LEFT;
+        case DisplayTopologyPosition::BOTTOM:
+            return DisplayTopologyPosition::TOP;
+    }
+}
+
+bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) {
+    return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID &&
+            displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId);
+}
+
+bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) {
+    for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) {
+        for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) {
+            const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId);
+            if (adjacentGraphIt == displayTopologyGraph.graph.end()) {
+                LOG(ERROR) << "Missing adjacent display in topology graph: "
+                           << adjacentDisplay.displayId << " for source " << sourceDisplay;
+                return false;
+            }
+            const auto reverseEdgeIt =
+                    std::find_if(adjacentGraphIt->second.begin(), adjacentGraphIt->second.end(),
+                                 [sourceDisplay](const DisplayTopologyAdjacentDisplay&
+                                                         reverseAdjacentDisplay) {
+                                     return sourceDisplay == reverseAdjacentDisplay.displayId;
+                                 });
+            if (reverseEdgeIt == adjacentGraphIt->second.end()) {
+                LOG(ERROR) << "Missing reverse edge in topology graph for: " << sourceDisplay
+                           << " -> " << adjacentDisplay.displayId;
+                return false;
+            }
+            DisplayTopologyPosition expectedPosition =
+                    getOppositePosition(adjacentDisplay.position);
+            if (reverseEdgeIt->position != expectedPosition) {
+                LOG(ERROR) << "Unexpected reverse edge for: " << sourceDisplay << " -> "
+                           << adjacentDisplay.displayId
+                           << " expected position: " << ftl::enum_string(expectedPosition)
+                           << " actual " << ftl::enum_string(reverseEdgeIt->position);
+                return false;
+            }
+            if (reverseEdgeIt->offsetDp != -adjacentDisplay.offsetDp) {
+                LOG(ERROR) << "Unexpected reverse edge offset: " << sourceDisplay << " -> "
+                           << adjacentDisplay.displayId
+                           << " expected offset: " << -adjacentDisplay.offsetDp << " actual "
+                           << reverseEdgeIt->offsetDp;
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) {
+    for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) {
+        if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) {
+            LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay;
+            return false;
+        }
+    }
+    return true;
+}
+
+std::string logicalDisplayIdToString(const ui::LogicalDisplayId& displayId) {
+    return base::StringPrintf("displayId(%d)", displayId.val());
+}
+
+std::string adjacentDisplayToString(const DisplayTopologyAdjacentDisplay& adjacentDisplay) {
+    return adjacentDisplay.dump();
+}
+
+std::string adjacentDisplayVectorToString(
+        const std::vector<DisplayTopologyAdjacentDisplay>& adjacentDisplays) {
+    return dumpVector(adjacentDisplays, adjacentDisplayToString);
+}
+
+} // namespace
+
+std::string DisplayTopologyAdjacentDisplay::dump() const {
+    std::string dump;
+    dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, "
+                               "offsetDp: %f}",
+                               displayId.val(), ftl::enum_string(position).c_str(), offsetDp);
+    return dump;
+}
+
+bool DisplayTopologyGraph::isValid() const {
+    return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) &&
+            validateDensities(*this);
+}
+
+std::string DisplayTopologyGraph::dump() const {
+    std::string dump;
+    dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val());
+    dump += base::StringPrintf("TopologyGraph:\n");
+    dump += addLinePrefix(dumpMap(graph, logicalDisplayIdToString, adjacentDisplayVectorToString),
+                          INDENT);
+    dump += "\n";
+    dump += base::StringPrintf("DisplaysDensity:\n");
+    dump += addLinePrefix(dumpMap(displaysDensity, logicalDisplayIdToString), INDENT);
+    dump += "\n";
+    return dump;
+}
+
+} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 65a088e..155ea00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -284,6 +284,36 @@
     return false;
 }
 
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+                        int32_t action) {
+    return isStylusEvent(source, properties) && isHoverAction(action);
+}
+
+bool isFromMouse(uint32_t source, ToolType toolType) {
+    return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::MOUSE;
+}
+
+bool isFromTouchpad(uint32_t source, ToolType toolType) {
+    return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::FINGER;
+}
+
+bool isFromDrawingTablet(uint32_t source, ToolType toolType) {
+    return isFromSource(source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
+            isStylusToolType(toolType);
+}
+
+bool isHoverAction(int32_t action) {
+    return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+            action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
+}
+
+bool isMouseOrTouchpad(uint32_t sources) {
+    // Check if this is a mouse or touchpad, but not a drawing tablet.
+    return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
+            (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
+             !isFromSource(sources, AINPUT_SOURCE_STYLUS));
+}
+
 VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
     return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
              event.getSource(), event.getDisplayId()},
diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp
new file mode 100644
index 0000000..6aa9ae6
--- /dev/null
+++ b/libs/input/InputFlags.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2025 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 <input/InputFlags.h>
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <com_android_window_flags.h>
+#include <cutils/properties.h>
+
+#include <string>
+
+namespace android {
+
+bool InputFlags::connectedDisplaysCursorEnabled() {
+    if (!com::android::window::flags::enable_desktop_mode_through_dev_option()) {
+        return com::android::input::flags::connected_displays_cursor();
+    }
+    static std::optional<bool> cachedDevOption;
+    if (!cachedDevOption.has_value()) {
+        char value[PROPERTY_VALUE_MAX];
+        constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts";
+        const int devOptionEnabled =
+                property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0;
+        cachedDevOption = devOptionEnabled == 1;
+    }
+    if (cachedDevOption.value_or(false)) {
+        return true;
+    }
+    return com::android::input::flags::connected_displays_cursor();
+}
+
+bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() {
+    return connectedDisplaysCursorEnabled() &&
+            com::android::input::flags::connected_displays_associated_display_cursor_bugfix();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 5bb30db..983bbde 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -26,13 +26,6 @@
 }
 
 flag {
-  name: "remove_input_channel_from_windowstate"
-  namespace: "input"
-  description: "Do not store a copy of input channel inside WindowState."
-  bug: "323450804"
-}
-
-flag {
   name: "enable_input_event_tracing"
   namespace: "input"
   description: "Set to true to enable input event tracing, including always-on tracing on non-user builds"
@@ -61,20 +54,6 @@
 }
 
 flag {
-  name: "report_palms_to_gestures_library"
-  namespace: "input"
-  description: "Report touches marked as palm by firmware to gestures library"
-  bug: "302505955"
-}
-
-flag {
-  name: "enable_touchpad_typing_palm_rejection"
-  namespace: "input"
-  description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
-  bug: "301055381"
-}
-
-flag {
   name: "enable_v2_touchpad_typing_palm_rejection"
   namespace: "input"
   description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click."
@@ -134,13 +113,6 @@
 }
 
 flag {
-  name: "hide_pointer_indicators_for_secure_windows"
-  namespace: "input"
-  description: "Hide touch and pointer indicators if a secure window is present on display"
-  bug: "325252005"
-}
-
-flag {
   name: "enable_keyboard_classifier"
   namespace: "input"
   description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
@@ -155,13 +127,6 @@
 }
 
 flag {
-  name: "include_relative_axis_values_for_captured_touchpads"
-  namespace: "input"
-  description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads."
-  bug: "330522990"
-}
-
-flag {
   name: "enable_per_device_input_latency_metrics"
   namespace: "input"
   description: "Capture input latency metrics on a per device granular level using histograms."
@@ -239,6 +204,16 @@
 }
 
 flag {
+  name: "connected_displays_associated_display_cursor_bugfix"
+  namespace: "lse_desktop_experience"
+  description: "Apply some rules to define associated display cursor behavior in connected displays"
+  bug: "396568321"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "use_cloned_screen_coordinates_as_raw"
   namespace: "input"
   description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
@@ -264,3 +239,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "enable_display_topology_validation"
+  namespace: "input"
+  description: "Set to true to enable display topology validation"
+  bug: "401219231"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 7638559..ee999f7 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -133,6 +133,12 @@
     flags: u32,
     button_state: u32,
 ) -> String {
+    let Some(converted_source) = Source::from_bits(source) else {
+        panic!(
+            "The conversion of source 0x{source:08x} failed, please check if some sources have not \
+             been added to Source."
+        );
+    };
     let Some(motion_flags) = MotionFlags::from_bits(flags) else {
         panic!(
             "The conversion of flags 0x{:08x} failed, please check if some flags have not been \
@@ -167,7 +173,7 @@
     }
     let result = verifier.process_movement(NotifyMotionArgs {
         device_id: DeviceId(device_id),
-        source: Source::from_bits(source).unwrap(),
+        source: converted_source,
         action: motion_action,
         pointer_properties,
         flags: motion_flags,
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 82ae422..08f0c5f 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,5 +1,4 @@
 mathias@google.com
-randolphs@google.com
 romainguy@google.com
 sumir@google.com
 jreck@google.com
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index bed31e2..89a97de 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -142,7 +142,7 @@
 }
 
 AChoreographer* AChoreographer_getInstance() {
-    return Choreographer_to_AChoreographer(Choreographer::getForThread());
+    return Choreographer_to_AChoreographer(Choreographer::getForThread().get());
 }
 
 void AChoreographer_postFrameCallback(AChoreographer* choreographer,
@@ -238,13 +238,17 @@
 }
 
 AChoreographer* AChoreographer_create() {
-    Choreographer* choreographer = new Choreographer(nullptr);
+    // Increments default strongRef count on construction, will be decremented on
+    // function exit.
+    auto choreographer = sp<Choreographer>::make(nullptr);
     status_t result = choreographer->initialize();
     if (result != OK) {
         ALOGW("Failed to initialize");
         return nullptr;
     }
-    return Choreographer_to_AChoreographer(choreographer);
+    // Will be decremented and destroyed by AChoreographer_destroy
+    choreographer->incStrong((void*)AChoreographer_create);
+    return Choreographer_to_AChoreographer(choreographer.get());
 }
 
 void AChoreographer_destroy(AChoreographer* choreographer) {
@@ -252,7 +256,7 @@
         return;
     }
 
-    delete AChoreographer_to_Choreographer(choreographer);
+    AChoreographer_to_Choreographer(choreographer)->decStrong((void*)AChoreographer_create);
 }
 
 int AChoreographer_getFd(const AChoreographer* choreographer) {
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 937ff02..51d0c81 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -50,14 +50,9 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-        mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN);
-        mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer());
-#else
-        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-        mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN);
-        mWindow = new TestableSurface(mProducer);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+        sp<Surface> surface;
+        std::tie(mItemConsumer, surface) = BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN);
+        mWindow = new TestableSurface(surface->getIGraphicBufferProducer());
         const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU);
         EXPECT_EQ(0, success);
     }
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7f207f0..f9b84fa 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -25,6 +25,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "renderengine_defaults",
+        "libsurfaceflinger_common_deps",
     ],
     cflags: [
         "-DGL_GLEXT_PROTOTYPES",
@@ -117,7 +118,10 @@
 // possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
 cc_defaults {
     name: "librenderengine_deps",
-    defaults: ["skia_renderengine_deps"],
+    defaults: [
+        "skia_renderengine_deps",
+        "libsurfaceflinger_common_deps",
+    ],
     static_libs: ["libskia_renderengine"],
 }
 
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index f84db0b..2d18ddb 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -28,6 +28,7 @@
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
         "surfaceflinger_defaults",
+        "libsurfaceflinger_common_deps",
     ],
     srcs: [
         "main.cpp",
@@ -38,7 +39,6 @@
     static_libs: [
         "librenderengine",
         "libshaders",
-        "libsurfaceflinger_common",
         "libtonemap",
     ],
     cflags: [
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index ac43da8..ecb16b2 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -301,6 +301,10 @@
         *os << "\n    .edgeExtensionEffect = " << settings.edgeExtensionEffect;
     }
     *os << "\n    .whitePointNits = " << settings.whitePointNits;
+    if (settings.luts) {
+        *os << "\n    .luts = ";
+        PrintTo(settings.luts, os);
+    }
     *os << "\n}";
 }
 
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 57041ee..f43694e 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 #include "Cache.h"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "AutoBackendTexture.h"
 #include "SkiaRenderEngine.h"
 #include "android-base/unique_fd.h"
@@ -28,6 +31,7 @@
 #include "utils/Timers.h"
 
 #include <com_android_graphics_libgui_flags.h>
+#include <common/trace.h>
 
 namespace android::renderengine::skia {
 
@@ -337,17 +341,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
+                            .boundaries = rect,
                             // The position transform doesn't matter when the reduced shader mode
                             // in in effect. A matrix transform stage is always included.
                             .positionTransform = mat4(),
-                            .boundaries = rect,
-                            .roundedCornersCrop = rect,
                             .roundedCornersRadius = {0.f, 0.f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
-                                                   .maxLuminanceNits = 1000.f,
                                                    .usePremultipliedAlpha = true,
-                                                   .isOpaque = true}},
+                                                   .isOpaque = true,
+                                                   .maxLuminanceNits = 1000.f}},
             .alpha = 1.f,
             .sourceDataspace = kDestDataSpace,
     };
@@ -370,16 +374,16 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
-                            .positionTransform = mat4(),
                             .boundaries = rect,
+                            .positionTransform = mat4(),
                             .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
                                                   .buffer = srcTexture,
-                                                  .maxLuminanceNits = 1000.f,
                                                   .usePremultipliedAlpha = true,
                                                   .isOpaque = false,
+                                                  .maxLuminanceNits = 1000.f,
                                           }},
             .sourceDataspace = kDestDataSpace,
     };
@@ -421,17 +425,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
-                            .positionTransform = mat4(),
                             .boundaries = boundary,
-                            .roundedCornersCrop = rect,
+                            .positionTransform = mat4(),
                             .roundedCornersRadius = {27.f, 27.f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
                                                   .buffer = srcTexture,
-                                                  .maxLuminanceNits = 1000.f,
                                                   .usePremultipliedAlpha = true,
                                                   .isOpaque = false,
+                                                  .maxLuminanceNits = 1000.f,
                                           }},
             .alpha = 1.f,
             .sourceDataspace = kDestDataSpace,
@@ -489,17 +493,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
+                            .boundaries = rect,
                             // The position transform doesn't matter when the reduced shader mode
                             // in in effect. A matrix transform stage is always included.
                             .positionTransform = mat4(),
-                            .boundaries = rect,
-                            .roundedCornersCrop = rect,
                             .roundedCornersRadius = {0.f, 0.f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
-                                                   .maxLuminanceNits = 1000.f,
                                                    .usePremultipliedAlpha = true,
-                                                   .isOpaque = true}},
+                                                   .isOpaque = true,
+                                                   .maxLuminanceNits = 1000.f}},
             .alpha = 1.f,
             .sourceDataspace = kBT2020DataSpace,
     };
@@ -527,17 +531,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
-                            .positionTransform = kScaleAsymmetric,
                             .boundaries = boundary,
-                            .roundedCornersCrop = rect,
+                            .positionTransform = kScaleAsymmetric,
                             .roundedCornersRadius = {64.1f, 64.1f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
                                                   .buffer = srcTexture,
-                                                  .maxLuminanceNits = 1000.f,
                                                   .usePremultipliedAlpha = true,
                                                   .isOpaque = true,
+                                                  .maxLuminanceNits = 1000.f,
                                           }},
             .alpha = 0.5f,
             .sourceDataspace = kBT2020DataSpace,
@@ -556,17 +560,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
+                            .boundaries = rect,
                             // The position transform doesn't matter when the reduced shader mode
                             // in in effect. A matrix transform stage is always included.
                             .positionTransform = mat4(),
-                            .boundaries = rect,
-                            .roundedCornersCrop = rect,
                             .roundedCornersRadius = {50.f, 50.f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
-                                                   .maxLuminanceNits = 1000.f,
                                                    .usePremultipliedAlpha = true,
-                                                   .isOpaque = true}},
+                                                   .isOpaque = true,
+                                                   .maxLuminanceNits = 1000.f}},
             .alpha = 0.5f,
             .sourceDataspace = kExtendedHdrDataSpce,
     };
@@ -594,17 +598,17 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
+                            .boundaries = rect,
                             // The position transform doesn't matter when the reduced shader mode
                             // in in effect. A matrix transform stage is always included.
                             .positionTransform = mat4(),
-                            .boundaries = rect,
-                            .roundedCornersCrop = rect,
                             .roundedCornersRadius = {50.f, 50.f},
+                            .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
-                                                   .maxLuminanceNits = 1000.f,
                                                    .usePremultipliedAlpha = true,
-                                                   .isOpaque = false}},
+                                                   .isOpaque = false,
+                                                   .maxLuminanceNits = 1000.f}},
             .alpha = 0.5f,
             .sourceDataspace = kOtherDataSpace,
     };
@@ -659,6 +663,7 @@
 // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
 //    gPrintSKSL = true
 void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) {
+    SFTRACE_CALL();
     const int previousCount = renderengine->reportShadersCompiled();
     if (previousCount) {
         ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
@@ -724,24 +729,29 @@
                                                impl::ExternalTexture::Usage::WRITEABLE);
 
         if (config.cacheHolePunchLayer) {
+            SFTRACE_NAME("cacheHolePunchLayer");
             drawHolePunchLayer(renderengine, display, dstTexture);
         }
 
         if (config.cacheSolidLayers) {
+            SFTRACE_NAME("cacheSolidLayers");
             drawSolidLayers(renderengine, display, dstTexture);
             drawSolidLayers(renderengine, p3Display, dstTexture);
         }
 
         if (config.cacheSolidDimmedLayers) {
+            SFTRACE_NAME("cacheSolidDimmedLayers");
             drawSolidDimmedLayers(renderengine, display, dstTexture);
         }
 
         if (config.cacheShadowLayers) {
+            SFTRACE_NAME("cacheShadowLayers");
             drawShadowLayers(renderengine, display, srcTexture);
             drawShadowLayers(renderengine, p3Display, srcTexture);
         }
 
         if (renderengine->supportsBackgroundBlur()) {
+            SFTRACE_NAME("supportsBackgroundBlur");
             drawBlurLayers(renderengine, display, dstTexture);
         }
 
@@ -776,32 +786,37 @@
 
         for (auto texture : textures) {
             if (config.cacheImageLayers) {
+                SFTRACE_NAME("cacheImageLayers");
                 drawImageLayers(renderengine, display, dstTexture, texture);
             }
 
             if (config.cacheImageDimmedLayers) {
+                SFTRACE_NAME("cacheImageDimmedLayers");
                 drawImageDimmedLayers(renderengine, display, dstTexture, texture);
                 drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture);
                 drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture);
             }
 
             if (config.cacheClippedLayers) {
+                SFTRACE_NAME("cacheClippedLayers");
                 // Draw layers for b/185569240.
                 drawClippedLayers(renderengine, display, dstTexture, texture);
             }
 
-            if (com::android::graphics::libgui::flags::edge_extension_shader() &&
-                config.cacheEdgeExtension) {
+            if (config.cacheEdgeExtension) {
+                SFTRACE_NAME("cacheEdgeExtension");
                 drawEdgeExtensionLayers(renderengine, display, dstTexture, texture);
                 drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture);
             }
         }
 
         if (config.cachePIPImageLayers) {
+            SFTRACE_NAME("cachePIPImageLayers");
             drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
         }
 
         if (config.cacheTransparentImageDimmedLayers) {
+            SFTRACE_NAME("cacheTransparentImageDimmedLayers");
             drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture,
                                              externalTexture);
             drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture);
@@ -811,10 +826,12 @@
         }
 
         if (config.cacheClippedDimmedImageLayers) {
+            SFTRACE_NAME("cacheClippedDimmedImageLayers");
             drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
         }
 
         if (config.cacheUltraHDR) {
+            SFTRACE_NAME("cacheUltraHDR");
             drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
 
             drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
@@ -833,7 +850,10 @@
         };
         auto layers = std::vector<LayerSettings>{layer};
         // call get() to make it synchronous
-        renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get();
+        {
+            SFTRACE_NAME("finalLayer");
+            renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get();
+        }
 
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 5f2d1b1..9e1c226 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -347,6 +347,7 @@
     if (useProtectedContextImpl(
             useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) {
         mInProtectedContext = useProtectedContext;
+        SFTRACE_INT("RE inProtectedContext", mInProtectedContext);
         // given that we are sharing the same thread between two contexts we need to
         // make sure that the thread state is reset when switching between the two.
         if (getActiveContext()) {
@@ -1235,6 +1236,16 @@
     LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
     auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
     trace(drawFence);
+    FenceTimePtr fenceTime = FenceTime::makeValid(drawFence);
+    for (const auto& layer : layers) {
+        if (FlagManager::getInstance().monitor_buffer_fences()) {
+            if (layer.source.buffer.buffer) {
+                layer.source.buffer.buffer->getBuffer()
+                        ->getDependencyMonitor()
+                        .addAccessCompletion(fenceTime, "RE");
+            }
+        }
+    }
     resultPromise->set_value(std::move(drawFence));
 }
 
diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp
index 11bcdb8..1c1ee0a 100644
--- a/libs/renderengine/skia/debug/CaptureTimer.cpp
+++ b/libs/renderengine/skia/debug/CaptureTimer.cpp
@@ -30,7 +30,7 @@
 
 void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) {
     this->clear = false;
-    CommonPool::post([=]() {
+    CommonPool::post([=,this]() {
         if (this->clear) return;
         std::this_thread::sleep_for(delay);
         if (this->clear) return;
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
index 4164c4b..f007427 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -58,9 +58,6 @@
 )");
 
 EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() {
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        return;
-    }
     mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader));
     LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(),
                         "EdgeExtensionShaderFactory compilation "
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
index f262158..6a577ff 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -24,7 +24,6 @@
 #include <ui/ColorSpace.h>
 
 #include "include/core/SkColorSpace.h"
-#include "src/core/SkColorFilterPriv.h"
 
 using aidl::android::hardware::graphics::composer3::LutProperties;
 
@@ -116,7 +115,7 @@
                 linear = mix(c0, c1, linear.b);
             }
         }
-        return float4(linear, rgba.a);
+        return float4(fromLinearSrgb(linear), rgba.a);
     })");
 
 // same as shader::toColorSpace function
@@ -289,9 +288,7 @@
                                       lutProperties[i].samplingKey, srcDataspace);
         }
 
-        auto colorXformLutToDst =
-                SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
-        input = input->makeWithColorFilter(colorXformLutToDst);
+        input = input->makeWithWorkingColorSpace(outColorSpace);
     }
     return input;
 }
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
index 800bf3c..271d7c8 100644
--- a/libs/tracing_perfetto/include/tracing_sdk.h
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -292,13 +292,8 @@
     arg_ = std::move(arg);
   }
 
-  ~DebugArg() {
-    free_string_value();
-  }
-
   void set_value(T value) {
     if constexpr (std::is_same_v<T, const char*>) {
-      free_string_value();
       arg_.value = value;
     } else if constexpr (std::is_same_v<T, int64_t>) {
       arg_.value = value;
@@ -321,16 +316,6 @@
   DISALLOW_COPY_AND_ASSIGN(DebugArg);
   TypeMap::type arg_;
   const std::string name_;
-
-  constexpr void free_string_value() {
-    if constexpr (std::is_same_v<typename TypeMap::type,
-                                 PerfettoTeHlExtraDebugArgString>) {
-      if (arg_.value) {
-        free((void*)arg_.value);
-        arg_.value = nullptr;
-      }
-    }
-  }
 };
 
 template <typename T>
@@ -375,10 +360,6 @@
     arg_ = std::move(arg);
   }
 
-  ~ProtoField() {
-    free_string_value();
-  }
-
   void set_value(uint32_t id, T value) {
     if constexpr (std::is_same_v<T, int64_t>) {
       arg_.header.id = id;
@@ -387,7 +368,6 @@
       arg_.header.id = id;
       arg_.value = value;
     } else if constexpr (std::is_same_v<T, const char*>) {
-      free_string_value();
       arg_.header.id = id;
       arg_.str = value;
     }
@@ -404,16 +384,6 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ProtoField);
   TypeMap::type arg_;
-
-  constexpr void free_string_value() {
-    if constexpr (std::is_same_v<typename TypeMap::type,
-                                 PerfettoTeHlProtoFieldCstr>) {
-      if (arg_.str) {
-        free((void*)arg_.str);
-        arg_.str = nullptr;
-      }
-    }
-  }
 };
 
 class ProtoFieldNested {
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
index 3e1ac2a..0e3fb07 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -25,8 +25,6 @@
 
 namespace internal {
 
-bool isPerfettoRegistered();
-
 struct PerfettoTeCategory* toPerfettoCategory(uint64_t category);
 
 void registerWithPerfetto(bool test = false);
diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp
index c97e900..70b8be9 100644
--- a/libs/tracing_perfetto/tracing_sdk.cpp
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -38,7 +38,7 @@
     PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type,
                          type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name,
                          extra->get());
-    extra->pop_extra();
+    extra->clear_extras();
   }
 }
 
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 87e213e..10cb992 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -122,6 +122,7 @@
 
     srcs: [
         "DebugUtils.cpp",
+        "DependencyMonitor.cpp",
         "DeviceProductInfo.cpp",
         "DisplayIdentification.cpp",
         "DynamicDisplayInfo.cpp",
diff --git a/libs/ui/DependencyMonitor.cpp b/libs/ui/DependencyMonitor.cpp
new file mode 100644
index 0000000..b7e490e
--- /dev/null
+++ b/libs/ui/DependencyMonitor.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 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_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "DependencyMonitor"
+
+#include <ui/DependencyMonitor.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+void DependencyMonitor::addIngress(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    resolveLocked();
+    if (mDependencies.isFull() && !mDependencies.front().updateSignalTimes(true)) {
+        ALOGD("%s: Clobbering unresolved dependencies -- make me bigger!", mToken.c_str());
+    }
+
+    auto& entry = mDependencies.next();
+    entry.reset(mToken.c_str());
+    ALOGV("%" PRId64 "/%s: addIngress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+          mToken.c_str(), systemTime(), annotation.c_str());
+
+    mDependencies.back().ingress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::addAccessCompletion(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    if (mDependencies.size() == 0) {
+        return;
+    }
+    ALOGV("%" PRId64 "/%s: addAccessCompletion at CPU time %" PRId64 " (%s)",
+          mDependencies.back().id, mToken.c_str(), systemTime(), annotation.c_str());
+    mDependencies.back().accessCompletions.emplace_back(std::move(fence), std::move(annotation));
+}
+
+void DependencyMonitor::addEgress(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    if (mDependencies.size() == 0) {
+        return;
+    }
+    ALOGV("%" PRId64 "/%s: addEgress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+          mToken.c_str(), systemTime(), annotation.c_str());
+    mDependencies.back().egress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::resolveLocked() {
+    if (mDependencies.size() == 0) {
+        return;
+    }
+
+    for (size_t i = mDependencies.size(); i > 0; i--) {
+        auto& dependencyBlock = mDependencies[i - 1];
+
+        if (dependencyBlock.validated) {
+            continue;
+        }
+
+        if (!dependencyBlock.updateSignalTimes(false)) {
+            break;
+        }
+
+        dependencyBlock.validated = true;
+        dependencyBlock.checkUnsafeAccess();
+    }
+}
+
+bool DependencyMonitor::DependencyBlock::updateSignalTimes(bool excludeIngress) {
+    if (egress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        return false;
+    }
+
+    if (!excludeIngress && ingress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        return false;
+    }
+
+    for (auto& accessCompletion : accessCompletions) {
+        if (accessCompletion.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void DependencyMonitor::DependencyBlock::checkUnsafeAccess() const {
+    const nsecs_t egressTime = egress.fence->getCachedSignalTime();
+    const nsecs_t ingressTime = ingress.fence->getCachedSignalTime();
+
+    ALOGV_IF(egressTime != Fence::SIGNAL_TIME_INVALID,
+             "%" PRId64 "/%s: Egress time: %" PRId64 " (%s)", token, id, egressTime,
+             egress.annotation.c_str());
+    ALOGV_IF(Fence::isValidTimestamp(egressTime) && Fence::isValidTimestamp(ingressTime) &&
+                     egressTime < ingressTime,
+             "%" PRId64 "/%s: Detected egress before ingress!: %" PRId64 " (%s) < %" PRId64 " (%s)",
+             id, token, egressTime, egress.annotation, ingressTime, ingress.annotation.c_str());
+
+    for (auto& accessCompletion : accessCompletions) {
+        const nsecs_t accessCompletionTime = accessCompletion.fence->getCachedSignalTime();
+        if (!Fence::isValidTimestamp(accessCompletionTime)) {
+            ALOGI("%" PRId64 "/%s: Detected invalid access completion! <%s>", id, token,
+                  accessCompletion.annotation.c_str());
+            continue;
+        } else {
+            ALOGV("%" PRId64 "/%s: Access completion time: %" PRId64 " <%s>", id, token,
+                  accessCompletionTime, accessCompletion.annotation.c_str());
+        }
+
+        ALOGI_IF(Fence::isValidTimestamp(egressTime) && accessCompletionTime > egressTime,
+                 "%" PRId64 "/%s: Detected access completion after egress!: %" PRId64
+                 " (%s) > %" PRId64 " (%s)",
+                 id, token, accessCompletionTime, accessCompletion.annotation.c_str(), egressTime,
+                 egress.annotation.c_str());
+
+        ALOGI_IF(Fence::isValidTimestamp(ingressTime) && accessCompletionTime < ingressTime,
+                 "%" PRId64 "/%s: Detected access completion prior to ingress!: %" PRId64
+                 " (%s) < %" PRId64 " (%s)",
+                 id, token, accessCompletionTime, accessCompletion.annotation.c_str(), ingressTime,
+                 ingress.annotation.c_str());
+    }
+
+    ALOGV_IF(ingressTime != Fence::SIGNAL_TIME_INVALID,
+             "%" PRId64 "/%s: Ingress time: %" PRId64 " (%s)", id, token, ingressTime,
+             ingress.annotation.c_str());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 4246c40..81afe9e 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -59,6 +59,14 @@
     }
 }
 
+FenceTimePtr FenceTime::makeValid(const sp<Fence>& fence) {
+    if (fence && fence->isValid()) {
+        return std::make_shared<FenceTime>(fence);
+    } else {
+        return std::make_shared<FenceTime>(systemTime());
+    }
+}
+
 void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
     if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
         // Applying Snapshot::State::FENCE, could change the valid state of the
@@ -289,9 +297,10 @@
 // ============================================================================
 void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
     std::lock_guard<std::mutex> lock(mMutex);
-    while (mQueue.size() >= MAX_ENTRIES) {
+    static constexpr size_t MAX_QUEUE_SIZE = 64;
+    while (mQueue.size() >= MAX_QUEUE_SIZE) {
         // This is a sanity check to make sure the queue doesn't grow unbounded.
-        // MAX_ENTRIES should be big enough not to trigger this path.
+        // MAX_QUEUE_SIZE should be big enough not to trigger this path.
         // In case this path is taken though, users of FenceTime must make sure
         // not to rely solely on FenceTimeline to get the final timestamp and
         // should eventually call Fence::getSignalTime on their own.
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 18c9a6b..f7c9400 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,6 +27,8 @@
 #include <ui/GraphicBufferMapper.h>
 #include <utils/Trace.h>
 
+#include <string>
+
 namespace android {
 
 // ===========================================================================
@@ -104,6 +106,7 @@
     usage  = 0;
     layerCount = 0;
     handle = nullptr;
+    mDependencyMonitor.setToken(std::to_string(mId));
 }
 
 // deprecated
@@ -155,6 +158,8 @@
         layerCount = request.layerCount;
         usage = request.usage;
         usage_deprecated = int(usage);
+        std::string name = request.requestorName;
+        mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
     }
 }
 
@@ -252,6 +257,7 @@
         usage = inUsage;
         usage_deprecated = int(usage);
         stride = static_cast<int>(outStride);
+        mDependencyMonitor.setToken(requestorName.append(":").append(std::to_string(mId)));
     }
     return err;
 }
@@ -609,6 +615,14 @@
         mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
     }
 
+    std::string name;
+    status_t err = mBufferMapper.getName(handle, &name);
+    if (err != NO_ERROR) {
+        name = "<Unknown>";
+    }
+
+    mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
+
     buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
     size -= sizeNeeded;
     fds += numFds;
diff --git a/libs/ui/include/ui/DependencyMonitor.h b/libs/ui/include/ui/DependencyMonitor.h
new file mode 100644
index 0000000..5ad1fd9
--- /dev/null
+++ b/libs/ui/include/ui/DependencyMonitor.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2025 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 <ui/FatVector.h>
+#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
+
+namespace android {
+
+// Debugging class for that tries to add userspace logging for fence depencencies.
+// The model that a DependencyMonitor tries to follow is, for each access of some resource:
+// 1. There is a single ingress fence, that guards whether a resource is now safe to read from
+// another system.
+// 2. There are multiple access fences, that are fired when a resource is read.
+// 3. There is a single egress fence, that is fired when a resource is released and sent to another
+// system.
+//
+// Note that there can be repeated ingress and egress of a resource, but the assumption is that
+// there is exactly one egress for every ingress, unless the resource is destroyed rather than
+// released.
+//
+// The DependencyMonitor will log if there is an anomaly in the fences tracked for some resource.
+// This includes:
+// * If (2) happens before (1)
+// * If (2) happens after (3)
+//
+// Note that this class has no knowledge of the "other system". I.e., if the other system ignores
+// the fence reported in (3), but still takes a long time to write to the resource and produce (1),
+// then nothing will be logged. That other system must have its own DependencyMonitor. Conversely,
+// this class has imperfect knowledge of the system it is monitoring. For example, this class does
+// not know the precise start times of reading from a resource, the exact time that a read might
+// occur from a hardware unit is not known to userspace.
+//
+// In other words, this class logs specific classes of fence violations, but is not sensitive to
+// *all* violations. One property of this is that unless the system tracked by a DependencyMonitor
+// is feeding in literally incorrect fences, then there is no chance of a false positive.
+//
+// This class is thread safe.
+class DependencyMonitor {
+public:
+    // Sets a debug token identifying the resource this monitor is tracking.
+    void setToken(std::string token) { mToken = std::move(token); }
+
+    // Adds a fence that is fired when the resource ready to be ingested by the system using the
+    // DependencyMonitor.
+    void addIngress(FenceTimePtr fence, std::string annotation);
+    // Adds a fence that is fired when the resource is accessed.
+    void addAccessCompletion(FenceTimePtr fence, std::string annotation);
+    // Adds a fence that is fired when the resource is released to another system.
+    void addEgress(FenceTimePtr fence, std::string annotation);
+
+private:
+    struct AnnotatedFenceTime {
+        FenceTimePtr fence;
+        std::string annotation;
+    };
+
+    struct DependencyBlock {
+        int64_t id = -1;
+        AnnotatedFenceTime ingress = {FenceTime::NO_FENCE, ""};
+        FatVector<AnnotatedFenceTime> accessCompletions;
+        AnnotatedFenceTime egress = {FenceTime::NO_FENCE, ""};
+        bool validated = false;
+        const char* token = nullptr;
+
+        void reset(const char* newToken) {
+            static std::atomic<int64_t> counter = 0;
+            id = counter++;
+            ingress = {FenceTime::NO_FENCE, ""};
+            accessCompletions.clear();
+            egress = {FenceTime::NO_FENCE, ""};
+            validated = false;
+            token = newToken;
+        }
+
+        // Returns true if all fences in this block have valid signal times.
+        bool updateSignalTimes(bool excludeIngress);
+
+        void checkUnsafeAccess() const;
+    };
+
+    void resolveLocked() REQUIRES(mMutex);
+
+    std::string mToken;
+    std::mutex mMutex;
+    ui::RingBuffer<DependencyBlock, 10> mDependencies GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 937e3f1..1e1c77b 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,6 +20,7 @@
 #include <ostream>
 #include <string>
 
+#include <ftl/match.h>
 #include <ftl/optional.h>
 
 namespace android {
@@ -36,7 +37,6 @@
     DisplayId& operator=(const DisplayId&) = default;
 
     static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); }
-    constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
 
     uint64_t value;
 
@@ -66,13 +66,6 @@
     // TODO: b/162612135 - Remove default constructor.
     PhysicalDisplayId() = default;
 
-    static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual()) {
-            return std::nullopt;
-        }
-        return PhysicalDisplayId(id);
-    }
-
     // Returns a stable ID based on EDID and port information.
     static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
                                                 uint32_t modelHash) {
@@ -90,8 +83,6 @@
         return PhysicalDisplayId(value);
     }
 
-    constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
-
 private:
     // Flag indicating that the ID is stable across reboots.
     static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
@@ -112,13 +103,6 @@
     // Flag indicating that this virtual display is backed by the GPU.
     static constexpr uint64_t FLAG_GPU = 1ULL << 61;
 
-    static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual()) {
-            return VirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr VirtualDisplayId fromValue(uint64_t value) {
         return VirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -134,13 +118,6 @@
 struct HalVirtualDisplayId : VirtualDisplayId {
     explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {}
 
-    static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual() && !(id.value & FLAG_GPU)) {
-            return HalVirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr HalVirtualDisplayId fromValue(uint64_t value) {
         return HalVirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -152,13 +129,6 @@
 struct GpuVirtualDisplayId : VirtualDisplayId {
     explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
 
-    static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual() && (id.value & FLAG_GPU)) {
-            return GpuVirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr GpuVirtualDisplayId fromValue(uint64_t value) {
         return GpuVirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -172,14 +142,6 @@
 struct HalDisplayId : DisplayId {
     constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
     constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
-
-    static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
-        if (GpuVirtualDisplayId::tryCast(id)) {
-            return std::nullopt;
-        }
-        return HalDisplayId(id);
-    }
-
     static constexpr HalDisplayId fromValue(uint64_t value) { return HalDisplayId(value); }
 
 private:
@@ -187,6 +149,47 @@
     explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
+using DisplayIdVariant = std::variant<PhysicalDisplayId, GpuVirtualDisplayId, HalVirtualDisplayId>;
+using VirtualDisplayIdVariant = std::variant<GpuVirtualDisplayId, HalVirtualDisplayId>;
+
+template <typename DisplayIdType>
+inline auto asDisplayIdOfType(DisplayIdVariant variant) -> ftl::Optional<DisplayIdType> {
+    return ftl::match(
+            variant,
+            [](DisplayIdType id) -> ftl::Optional<DisplayIdType> { return ftl::Optional(id); },
+            [](auto) -> ftl::Optional<DisplayIdType> { return std::nullopt; });
+}
+
+template <typename Variant>
+inline auto asHalDisplayId(Variant variant) -> ftl::Optional<HalDisplayId> {
+    return ftl::match(
+            variant,
+            [](GpuVirtualDisplayId) -> ftl::Optional<HalDisplayId> { return std::nullopt; },
+            [](auto id) -> ftl::Optional<HalDisplayId> {
+                return ftl::Optional(static_cast<HalDisplayId>(id));
+            });
+}
+
+inline auto asPhysicalDisplayId(DisplayIdVariant variant) -> ftl::Optional<PhysicalDisplayId> {
+    return asDisplayIdOfType<PhysicalDisplayId>(variant);
+}
+
+inline auto asVirtualDisplayId(DisplayIdVariant variant) -> ftl::Optional<VirtualDisplayId> {
+    return ftl::match(
+            variant,
+            [](GpuVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+                return ftl::Optional(static_cast<VirtualDisplayId>(id));
+            },
+            [](HalVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+                return ftl::Optional(static_cast<VirtualDisplayId>(id));
+            },
+            [](auto) -> ftl::Optional<VirtualDisplayId> { return std::nullopt; });
+}
+
+inline auto asDisplayId(DisplayIdVariant variant) -> DisplayId {
+    return ftl::match(variant, [](auto id) -> DisplayId { return static_cast<DisplayId>(id); });
+}
+
 static_assert(sizeof(DisplayId) == sizeof(uint64_t));
 static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 334106f..3560d57 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_FENCE_TIME_H
 #define ANDROID_FENCE_TIME_H
 
+#include <stddef.h>
 #include <ui/Fence.h>
 #include <utils/Flattenable.h>
 #include <utils/Mutex.h>
@@ -30,6 +31,8 @@
 namespace android {
 
 class FenceToFenceTimeMap;
+class FenceTime;
+using FenceTimePtr = std::shared_ptr<FenceTime>;
 
 // A wrapper around fence that only implements isValid and getSignalTime.
 // It automatically closes the fence in a thread-safe manner once the signal
@@ -95,6 +98,10 @@
     FenceTime& operator=(const FenceTime&) = delete;
     FenceTime& operator=(FenceTime&&) = delete;
 
+    // Constructs a FenceTime, falling back to a timestamp if the fence is
+    // invalid.
+    static FenceTimePtr makeValid(const sp<Fence>& fence);
+
     // This method should only be called when replacing the fence with
     // a signalTime. Since this is an indirect way of setting the signal time
     // of a fence, the snapshot should come from a trusted source.
@@ -142,8 +149,6 @@
     std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
 };
 
-using FenceTimePtr = std::shared_ptr<FenceTime>;
-
 // A queue of FenceTimes that are expected to signal in FIFO order.
 // Only maintains a queue of weak pointers so it doesn't keep references
 // to Fences on its own.
@@ -162,8 +167,6 @@
 // different threads.
 class FenceTimeline {
 public:
-    static constexpr size_t MAX_ENTRIES = 64;
-
     void push(const std::shared_ptr<FenceTime>& fence);
     void updateSignalTimes();
 
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 936bf8f..9305180 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -23,6 +23,7 @@
 #include <string>
 #include <utility>
 #include <vector>
+#include "ui/DependencyMonitor.h"
 
 #include <android/hardware_buffer.h>
 #include <ui/ANativeObjectBase.h>
@@ -229,6 +230,8 @@
 
     void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
 
+    DependencyMonitor& getDependencyMonitor() { return mDependencyMonitor; }
+
 private:
     ~GraphicBuffer();
 
@@ -295,6 +298,8 @@
     // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
     std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
             mDeathCallbacks;
+
+    DependencyMonitor mDependencyMonitor;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/libs/ui/include/ui/RingBuffer.h
similarity index 93%
rename from services/surfaceflinger/Utils/RingBuffer.h
rename to libs/ui/include/ui/RingBuffer.h
index 215472b..31d5a95 100644
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ b/libs/ui/include/ui/RingBuffer.h
@@ -19,7 +19,7 @@
 #include <stddef.h>
 #include <array>
 
-namespace android::utils {
+namespace android::ui {
 
 template <class T, size_t SIZE>
 class RingBuffer {
@@ -31,8 +31,8 @@
     ~RingBuffer() = default;
 
     constexpr size_t capacity() const { return SIZE; }
-
     size_t size() const { return mCount; }
+    bool isFull() const { return size() == capacity(); }
 
     T& next() {
         mHead = static_cast<size_t>(mHead + 1) % SIZE;
@@ -67,4 +67,4 @@
     size_t mCount = 0;
 };
 
-} // namespace android::utils
+} // namespace android::ui
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index 83da821..5316448 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -28,6 +28,7 @@
 // Immutable information about physical display.
 struct StaticDisplayInfo {
     DisplayConnectionType connectionType = DisplayConnectionType::Internal;
+    uint8_t port;
     float density = 0.f;
     bool secure = false;
     std::optional<DeviceProductInfo> deviceProductInfo;
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
index 70c50f0..98018d9 100644
--- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -36,7 +36,7 @@
  */
 inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace,
                                       std::optional<ui::PixelFormat> pixelFormat,
-                                      float hdrSdrRatio = 1.f) {
+                                      float hdrSdrRatio = 1.f, bool hasHdrMetadata = false) {
     const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
     const auto range = dataspace & HAL_DATASPACE_RANGE_MASK;
 
@@ -49,7 +49,8 @@
                                                                      HAL_DATASPACE_RANGE_EXTENDED);
 
     if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) &&
-        pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) {
+        pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16 &&
+        hasHdrMetadata) {
         return HdrRenderType::GENERIC_HDR;
     }
 
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 2d8a1e3..d950f2a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -45,16 +45,6 @@
 }
 
 cc_test {
-    name: "DisplayId_test",
-    shared_libs: ["libui"],
-    srcs: ["DisplayId_test.cpp"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_test {
     name: "DisplayIdentification_test",
     shared_libs: ["libui"],
     static_libs: ["libgmock"],
@@ -144,6 +134,17 @@
 }
 
 cc_test {
+    name: "RingBuffer_test",
+    test_suites: ["device-tests"],
+    shared_libs: ["libui"],
+    srcs: ["RingBuffer_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
     name: "Size_test",
     test_suites: ["device-tests"],
     shared_libs: ["libui"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
deleted file mode 100644
index 209acba..0000000
--- a/libs/ui/tests/DisplayId_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include <ui/DisplayId.h>
-
-#include <gtest/gtest.h>
-
-namespace android::ui {
-
-TEST(DisplayIdTest, createPhysicalIdFromEdid) {
-    constexpr uint8_t port = 1;
-    constexpr uint16_t manufacturerId = 13;
-    constexpr uint32_t modelHash = 42;
-    const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
-    EXPECT_EQ(port, id.getPort());
-    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createPhysicalIdFromPort) {
-    constexpr uint8_t port = 3;
-    const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
-    EXPECT_EQ(port, id.getPort());
-    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createGpuVirtualId) {
-    const GpuVirtualDisplayId id(42);
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_FALSE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, GpuVirtualDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) {
-    const VirtualDisplayId id(GpuVirtualDisplayId(42));
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_FALSE(HalDisplayId::tryCast(id));
-
-    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
-    EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
-}
-
-TEST(DisplayIdTest, createHalVirtualId) {
-    const HalVirtualDisplayId id(42);
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, HalVirtualDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) {
-    const VirtualDisplayId id(HalVirtualDisplayId(42));
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
-    EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value());
-}
-
-} // namespace android::ui
diff --git a/libs/ui/tests/RingBuffer_test.cpp b/libs/ui/tests/RingBuffer_test.cpp
new file mode 100644
index 0000000..9839492
--- /dev/null
+++ b/libs/ui/tests/RingBuffer_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 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 <gtest/gtest.h>
+#include <ui/RingBuffer.h>
+
+namespace android::ui {
+
+TEST(RingBuffer, basic) {
+    RingBuffer<int, 5> rb;
+
+    rb.next() = 1;
+    ASSERT_EQ(1, rb.size());
+    ASSERT_EQ(1, rb.back());
+    ASSERT_EQ(1, rb.front());
+
+    rb.next() = 2;
+    ASSERT_EQ(2, rb.size());
+    ASSERT_EQ(2, rb.back());
+    ASSERT_EQ(1, rb.front());
+    ASSERT_EQ(1, rb[-1]);
+
+    rb.next() = 3;
+    ASSERT_EQ(3, rb.size());
+    ASSERT_EQ(3, rb.back());
+    ASSERT_EQ(1, rb.front());
+    ASSERT_EQ(2, rb[-1]);
+    ASSERT_EQ(1, rb[-2]);
+
+    rb.next() = 4;
+    ASSERT_EQ(4, rb.size());
+    ASSERT_EQ(4, rb.back());
+    ASSERT_EQ(1, rb.front());
+    ASSERT_EQ(3, rb[-1]);
+    ASSERT_EQ(2, rb[-2]);
+    ASSERT_EQ(1, rb[-3]);
+
+    rb.next() = 5;
+    ASSERT_EQ(5, rb.size());
+    ASSERT_EQ(5, rb.back());
+    ASSERT_EQ(1, rb.front());
+    ASSERT_EQ(4, rb[-1]);
+    ASSERT_EQ(3, rb[-2]);
+    ASSERT_EQ(2, rb[-3]);
+    ASSERT_EQ(1, rb[-4]);
+
+    rb.next() = 6;
+    ASSERT_EQ(5, rb.size());
+    ASSERT_EQ(6, rb.back());
+    ASSERT_EQ(2, rb.front());
+    ASSERT_EQ(5, rb[-1]);
+    ASSERT_EQ(4, rb[-2]);
+    ASSERT_EQ(3, rb[-3]);
+    ASSERT_EQ(2, rb[-4]);
+}
+
+} // namespace android::ui
\ No newline at end of file
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 04c525e..7faf361 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -356,7 +356,7 @@
 
     // If we're going to be over the cache limit, kick off a trim to clear space
     if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
-        ALOGV("SET: Cache is full, calling trimCache to clear space");
+        ALOGW("SET: Cache is full, calling trimCache to clear space");
         trimCache();
     }
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0dd9f19..efc34f6 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -146,7 +146,7 @@
             }
         }
 
-        if (graphicsenv_flags::feature_overrides()) {
+        if (graphicsenv_flags::angle_feature_overrides()) {
             // Get the list of ANGLE features to enable from Global.Settings.
             const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
             for (const std::string& eglFeature : eglFeatures) {
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index e94b565..beac900 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -29,7 +29,7 @@
 WindowSurface::WindowSurface() {
     status_t err;
 
-    sp<SurfaceComposerClient> surfaceComposerClient = new SurfaceComposerClient;
+    sp<SurfaceComposerClient> surfaceComposerClient = sp<SurfaceComposerClient>::make();
     err = surfaceComposerClient->initCheck();
     if (err != NO_ERROR) {
         fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err);
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index afa6233..56c3b7d 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -65,7 +65,7 @@
             std::swap(displayWidth, displayHeight);
         }
 
-        sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient();
+        sp<android::SurfaceComposerClient> surfaceClient = sp<SurfaceComposerClient>::make();
         err = surfaceClient->initCheck();
         if (err != NO_ERROR) {
             ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 01287b0..74e354f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -39,6 +39,7 @@
 cc_defaults {
     name: "libgpuservice_defaults",
     defaults: [
+        "aconfig_lib_cc_static_link.defaults",
         "gpuservice_defaults",
         "libfeatureoverride_deps",
         "libgfxstats_deps",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index f74b4fa..46327df 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -25,8 +25,10 @@
 #include <binder/Parcel.h>
 #include <binder/PermissionCache.h>
 #include <com_android_frameworks_gpuservice_flags.h>
+#include <com_android_graphics_graphicsenv_flags.h>
 #include <cutils/properties.h>
 #include <cutils/multiuser.h>
+#include <feature_override/FeatureOverrideParser.h>
 #include <gpumem/GpuMem.h>
 #include <gpuwork/GpuWork.h>
 #include <gpustats/GpuStats.h>
@@ -41,6 +43,7 @@
 #include <memory>
 
 namespace gpuservice_flags = com::android::frameworks::gpuservice::flags;
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
 
 namespace android {
 
@@ -143,6 +146,14 @@
     }
 }
 
+FeatureOverrides GpuService::getFeatureOverrides() {
+    if (!graphicsenv_flags::angle_feature_overrides()) {
+        FeatureOverrides featureOverrides;
+        return featureOverrides;
+    }
+
+    return mFeatureOverrideParser.getFeatureOverrides();
+}
 
 void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
     IPCThreadState* ipc = IPCThreadState::self();
@@ -171,7 +182,11 @@
     for (size_t i = 0, n = args.size(); i < n; i++)
         ALOGV("  arg[%zu]: '%s'", i, String8(args[i]).c_str());
 
-    if (args.size() >= 1) {
+    if (!args.empty()) {
+        if (graphicsenv_flags::angle_feature_overrides()) {
+            if (args[0] == String16("featureOverrides"))
+                return cmdFeatureOverrides(out, err);
+        }
         if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
         if (args[0] == String16("vkprofiles")) return cmdVkprofiles(out, err);
         if (args[0] == String16("help")) return cmdHelp(out);
@@ -235,6 +250,11 @@
     return NO_ERROR;
 }
 
+status_t GpuService::cmdFeatureOverrides(int out, int /*err*/) {
+    dprintf(out, "%s\n", mFeatureOverrideParser.getFeatureOverrides().toString().c_str());
+    return NO_ERROR;
+}
+
 namespace {
 
 status_t cmdHelp(int out) {
@@ -247,6 +267,10 @@
             "GPU Service commands:\n"
             "  vkjson      dump Vulkan properties as JSON\n"
             "  vkprofiles  print support for select Vulkan profiles\n");
+    if (graphicsenv_flags::angle_feature_overrides()) {
+        fprintf(outs,
+                "  featureOverrides  update and output gpuservice's feature overrides\n");
+    }
     fclose(outs);
     return NO_ERROR;
 }
diff --git a/services/gpuservice/feature_override/Android.bp b/services/gpuservice/feature_override/Android.bp
index 3b5407b..842a0c4 100644
--- a/services/gpuservice/feature_override/Android.bp
+++ b/services/gpuservice/feature_override/Android.bp
@@ -69,6 +69,7 @@
     name: "libfeatureoverride",
     defaults: [
         "libfeatureoverride_deps",
+        "libvkjson_deps",
     ],
     srcs: [
         ":feature_config_proto_definitions",
@@ -85,6 +86,9 @@
     cppflags: [
         "-Wno-sign-compare",
     ],
+    static_libs: [
+        "libvkjson",
+    ],
     export_include_dirs: ["include"],
     proto: {
         type: "lite",
diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
index a16bfa8..26ff84a 100644
--- a/services/gpuservice/feature_override/FeatureOverrideParser.cpp
+++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
@@ -23,8 +23,10 @@
 #include <sys/stat.h>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <graphicsenv/FeatureOverrides.h>
 #include <log/log.h>
+#include <vkjson.h>
 
 #include "feature_config.pb.h"
 
@@ -35,13 +37,53 @@
     featureOverrides.mPackageFeatures.clear();
 }
 
+bool
+gpuVendorIdMatches(const VkJsonInstance &vkJsonInstance,
+               const uint32_t &configVendorId) {
+    if (vkJsonInstance.devices.empty()) {
+        return false;
+    }
+
+    // Always match the TEST Vendor ID
+    if (configVendorId == feature_override::GpuVendorID::VENDOR_ID_TEST) {
+        return true;
+    }
+
+    // Always assume one GPU device.
+    uint32_t vendorID = vkJsonInstance.devices.front().properties.vendorID;
+
+    return vendorID == configVendorId;
+}
+
+bool
+conditionsMet(const VkJsonInstance &vkJsonInstance,
+              const android::FeatureConfig &featureConfig) {
+    bool gpuVendorIdMatch = false;
+
+    if (featureConfig.mGpuVendorIDs.empty()) {
+        gpuVendorIdMatch = true;
+    } else {
+        for (const auto &gpuVendorID: featureConfig.mGpuVendorIDs) {
+            if (gpuVendorIdMatches(vkJsonInstance, gpuVendorID)) {
+                gpuVendorIdMatch = true;
+                break;
+            }
+        }
+    }
+
+    return gpuVendorIdMatch;
+}
+
 void initFeatureConfig(android::FeatureConfig &featureConfig,
                        const feature_override::FeatureConfig &featureConfigProto) {
     featureConfig.mFeatureName = featureConfigProto.feature_name();
     featureConfig.mEnabled = featureConfigProto.enabled();
+    for (const auto &gpuVendorIdProto: featureConfigProto.gpu_vendor_ids()) {
+        featureConfig.mGpuVendorIDs.emplace_back(static_cast<uint32_t>(gpuVendorIdProto));
+    }
 }
 
-feature_override::FeatureOverrideProtos readFeatureConfigProtos(std::string configFilePath) {
+feature_override::FeatureOverrideProtos readFeatureConfigProtos(const std::string &configFilePath) {
     feature_override::FeatureOverrideProtos overridesProtos;
 
     std::ifstream protobufBinaryFile(configFilePath.c_str());
@@ -78,9 +120,15 @@
 
 bool FeatureOverrideParser::shouldReloadFeatureOverrides() const {
     std::string configFilePath = getFeatureOverrideFilePath();
+
+    std::ifstream configFile(configFilePath);
+    if (!configFile.good()) {
+        return false;
+    }
+
     struct stat fileStat{};
-    if (stat(getFeatureOverrideFilePath().c_str(), &fileStat) != 0) {
-        ALOGE("Error getting file information for '%s': %s", getFeatureOverrideFilePath().c_str(),
+    if (stat(configFilePath.c_str(), &fileStat) != 0) {
+        ALOGE("Error getting file information for '%s': %s", configFilePath.c_str(),
               strerror(errno));
         // stat'ing the file failed, so return false since reading it will also likely fail.
         return false;
@@ -100,12 +148,22 @@
     // Clear out the stale values before adding the newly parsed data.
     resetFeatureOverrides(mFeatureOverrides);
 
+    if (overridesProtos.global_features().empty() &&
+        overridesProtos.package_features().empty()) {
+        // No overrides to parse.
+        return;
+    }
+
+    const VkJsonInstance vkJsonInstance = VkJsonGetInstance();
+
     // Global feature overrides.
     for (const auto &featureConfigProto: overridesProtos.global_features()) {
         FeatureConfig featureConfig;
         initFeatureConfig(featureConfig, featureConfigProto);
 
-        mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
+        if (conditionsMet(vkJsonInstance, featureConfig)) {
+            mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
+        }
     }
 
     // App-specific feature overrides.
@@ -122,7 +180,9 @@
             FeatureConfig featureConfig;
             initFeatureConfig(featureConfig, featureConfigProto);
 
-            featureConfigs.emplace_back(featureConfig);
+            if (conditionsMet(vkJsonInstance, featureConfig)) {
+                featureConfigs.emplace_back(featureConfig);
+            }
         }
 
         mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs;
diff --git a/services/gpuservice/feature_override/proto/feature_config.proto b/services/gpuservice/feature_override/proto/feature_config.proto
index 4d4bf28..f285187 100644
--- a/services/gpuservice/feature_override/proto/feature_config.proto
+++ b/services/gpuservice/feature_override/proto/feature_config.proto
@@ -21,14 +21,43 @@
 option optimize_for = LITE_RUNTIME;
 
 /**
+ * GPU Vendor IDs.
+ * Taken from: external/angle/src/libANGLE/renderer/driver_utils.h
+ */
+enum GpuVendorID
+{
+    // Test ID matches every GPU Vendor ID.
+    VENDOR_ID_TEST = 0;
+    VENDOR_ID_AMD = 0x1002;
+    VENDOR_ID_ARM = 0x13B5;
+    // Broadcom devices won't use PCI, but this is their Vulkan vendor id.
+    VENDOR_ID_BROADCOM = 0x14E4;
+    VENDOR_ID_GOOGLE = 0x1AE0;
+    VENDOR_ID_INTEL = 0x8086;
+    VENDOR_ID_MESA = 0x10005;
+    VENDOR_ID_MICROSOFT = 0x1414;
+    VENDOR_ID_NVIDIA = 0x10DE;
+    VENDOR_ID_POWERVR = 0x1010;
+    // This is Qualcomm PCI Vendor ID.
+    // Android doesn't have a PCI bus, but all we need is a unique id.
+    VENDOR_ID_QUALCOMM = 0x5143;
+    VENDOR_ID_SAMSUNG = 0x144D;
+    VENDOR_ID_VIVANTE = 0x9999;
+    VENDOR_ID_VMWARE = 0x15AD;
+    VENDOR_ID_VIRTIO = 0x1AF4;
+}
+
+/**
  * Feature Configuration
  * feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h).
  * enabled: Either enable or disable the feature.
+ * gpu_vendor_ids: The GPU architectures this FeatureConfig applies to, if any.
  */
 message FeatureConfig
 {
     string feature_name         = 1;
     bool enabled                = 2;
+    repeated GpuVendorID gpu_vendor_ids = 3;
 }
 
 /**
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 057d127..22be9a7 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -20,6 +20,7 @@
 #include <binder/IInterface.h>
 #include <cutils/compiler.h>
 #include <feature_override/FeatureOverrideParser.h>
+#include <graphicsenv/FeatureOverrides.h>
 #include <graphicsenv/GpuStatsInfo.h>
 #include <graphicsenv/IGpuService.h>
 #include <serviceutils/PriorityDumper.h>
@@ -64,6 +65,7 @@
                         const uint64_t* values, const uint32_t valueCount) override;
     void setUpdatableDriverPath(const std::string& driverPath) override;
     std::string getUpdatableDriverPath() override;
+    FeatureOverrides getFeatureOverrides() override;
     void toggleAngleAsSystemDriver(bool enabled) override;
     void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode,
                              const char *engineName) override;
@@ -86,6 +88,8 @@
 
     status_t doDump(int fd, const Vector<String16>& args, bool asProto);
 
+    status_t cmdFeatureOverrides(int out, int /*err*/);
+
     /*
      * Attributes
      */
diff --git a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
index 65a1b58..66556cd 100644
--- a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
+++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
@@ -70,14 +70,14 @@
 };
 
 testing::AssertionResult validateFeatureConfigTestTxtpbSizes(FeatureOverrides overrides) {
-    size_t expectedGlobalFeaturesSize = 1;
+    size_t expectedGlobalFeaturesSize = 3;
     if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) {
         return testing::AssertionFailure()
                 << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size()
                 << ", expected: " << expectedGlobalFeaturesSize;
     }
 
-    size_t expectedPackageFeaturesSize = 1;
+    size_t expectedPackageFeaturesSize = 3;
     if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) {
         return testing::AssertionFailure()
                 << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size()
@@ -133,6 +133,96 @@
     EXPECT_TRUE(validateGlobalOverrides1(overrides));
 }
 
+testing::AssertionResult validateGlobalOverrides2(FeatureOverrides overrides) {
+    const int kTestFeatureIndex = 1;
+    const std::string expectedFeatureName = "globalOverrides2";
+    const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+    if (cfg.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
+    bool expectedEnabled = true;
+    if (cfg.mEnabled != expectedEnabled) {
+        return testing::AssertionFailure()
+                << "cfg.mEnabled: " << cfg.mEnabled
+                << ", expected: " << expectedEnabled;
+    }
+
+    std::vector<uint32_t> expectedGpuVendorIDs = {
+        0,      // GpuVendorID::VENDOR_ID_TEST
+        0x13B5, // GpuVendorID::VENDOR_ID_ARM
+    };
+    if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+        return testing::AssertionFailure()
+                << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+                << ", expected: " << expectedGpuVendorIDs.size();
+    }
+    for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+        if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+            std::stringstream msg;
+            msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+                << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+            return testing::AssertionFailure() << msg.str();
+        }
+    }
+
+    return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides2) {
+    FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+    EXPECT_TRUE(validateGlobalOverrides2(overrides));
+}
+
+testing::AssertionResult validateGlobalOverrides3(FeatureOverrides overrides) {
+    const int kTestFeatureIndex = 2;
+    const std::string expectedFeatureName = "globalOverrides3";
+    const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+    if (cfg.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
+    bool expectedEnabled = true;
+    if (cfg.mEnabled != expectedEnabled) {
+        return testing::AssertionFailure()
+                << "cfg.mEnabled: " << cfg.mEnabled
+                << ", expected: " << expectedEnabled;
+    }
+
+    std::vector<uint32_t> expectedGpuVendorIDs = {
+            0,      // GpuVendorID::VENDOR_ID_TEST
+            0x8086, // GpuVendorID::VENDOR_ID_INTEL
+    };
+    if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+        return testing::AssertionFailure()
+                << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+                << ", expected: " << expectedGpuVendorIDs.size();
+    }
+    for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+        if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+            std::stringstream msg;
+            msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+                << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+            return testing::AssertionFailure() << msg.str();
+        }
+    }
+
+    return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validateGlobalOverrides3(overrides));
+}
+
 testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) {
     const std::string expectedTestPackageName = "com.gpuservice_unittest.packageOverrides1";
 
@@ -155,6 +245,12 @@
     const std::string expectedFeatureName = "packageOverrides1";
     const FeatureConfig &cfg = features[0];
 
+    if (cfg.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
     bool expectedEnabled = true;
     if (cfg.mEnabled != expectedEnabled) {
         return testing::AssertionFailure()
@@ -193,6 +289,160 @@
     return testing::AssertionSuccess();
 }
 
+testing::AssertionResult validatePackageOverrides2(FeatureOverrides overrides) {
+    const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides2";
+
+    if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+        return testing::AssertionFailure()
+                << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+    }
+
+    const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+    size_t expectedFeaturesSize = 1;
+    if (features.size() != expectedFeaturesSize) {
+        return testing::AssertionFailure()
+                << "features.size(): " << features.size()
+                << ", expectedFeaturesSize: " << expectedFeaturesSize;
+    }
+
+    const std::string expectedFeatureName = "packageOverrides2";
+    const FeatureConfig &cfg = features[0];
+
+    if (cfg.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
+    bool expectedEnabled = false;
+    if (cfg.mEnabled != expectedEnabled) {
+        return testing::AssertionFailure()
+                << "cfg.mEnabled: " << cfg.mEnabled
+                << ", expected: " << expectedEnabled;
+    }
+
+    std::vector<uint32_t> expectedGpuVendorIDs = {
+            0,      // GpuVendorID::VENDOR_ID_TEST
+            0x8086, // GpuVendorID::VENDOR_ID_INTEL
+    };
+    if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+        return testing::AssertionFailure()
+                << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+                << ", expected: " << expectedGpuVendorIDs.size();
+    }
+    for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+        if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+            std::stringstream msg;
+            msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+                << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+            return testing::AssertionFailure() << msg.str();
+        }
+    }
+
+    return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides2) {
+    FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+    EXPECT_TRUE(validatePackageOverrides2(overrides));
+}
+
+testing::AssertionResult validatePackageOverrides3(FeatureOverrides overrides) {
+    const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides3";
+
+    if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+        return testing::AssertionFailure()
+                << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+    }
+
+    const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+    size_t expectedFeaturesSize = 2;
+    if (features.size() != expectedFeaturesSize) {
+        return testing::AssertionFailure()
+                << "features.size(): " << features.size()
+                << ", expectedFeaturesSize: " << expectedFeaturesSize;
+    }
+
+    std::string expectedFeatureName = "packageOverrides3_1";
+    const FeatureConfig &cfg_1 = features[0];
+
+    if (cfg_1.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg_1.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
+    bool expectedEnabled = false;
+    if (cfg_1.mEnabled != expectedEnabled) {
+        return testing::AssertionFailure()
+                << "cfg.mEnabled: " << cfg_1.mEnabled
+                << ", expected: " << expectedEnabled;
+    }
+
+    std::vector<uint32_t> expectedGpuVendorIDs = {
+            0,      // GpuVendorID::VENDOR_ID_TEST
+            0x13B5, // GpuVendorID::VENDOR_ID_ARM
+    };
+    if (cfg_1.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+        return testing::AssertionFailure()
+                << "cfg.mGpuVendorIDs.size(): " << cfg_1.mGpuVendorIDs.size()
+                << ", expected: " << expectedGpuVendorIDs.size();
+    }
+    for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+        if (cfg_1.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+            std::stringstream msg;
+            msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_1.mGpuVendorIDs[i]
+                << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+            return testing::AssertionFailure() << msg.str();
+        }
+    }
+
+    expectedFeatureName = "packageOverrides3_2";
+    const FeatureConfig &cfg_2 = features[1];
+
+    if (cfg_2.mFeatureName != expectedFeatureName) {
+        return testing::AssertionFailure()
+                << "cfg.mFeatureName: " << cfg_2.mFeatureName
+                << ", expected: " << expectedFeatureName;
+    }
+
+    expectedEnabled = true;
+    if (cfg_2.mEnabled != expectedEnabled) {
+        return testing::AssertionFailure()
+                << "cfg.mEnabled: " << cfg_2.mEnabled
+                << ", expected: " << expectedEnabled;
+    }
+
+    expectedGpuVendorIDs = {
+            0,      // GpuVendorID::VENDOR_ID_TEST
+            0x8086, // GpuVendorID::VENDOR_ID_INTEL
+    };
+    if (cfg_2.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+        return testing::AssertionFailure()
+                << "cfg.mGpuVendorIDs.size(): " << cfg_2.mGpuVendorIDs.size()
+                << ", expected: " << expectedGpuVendorIDs.size();
+    }
+    for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+        if (cfg_2.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+            std::stringstream msg;
+            msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_2.mGpuVendorIDs[i]
+                << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+            return testing::AssertionFailure() << msg.str();
+        }
+    }
+
+    return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validatePackageOverrides3(overrides));
+}
+
 TEST_F(FeatureOverrideParserTest, forceFileRead) {
     FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
 
diff --git a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
index 726779e..44a6f78 100644
--- a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
+++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
@@ -22,6 +22,22 @@
     {
         feature_name: "globalOverrides1"
         enabled: False
+    },
+    {
+        feature_name: "globalOverrides2"
+        enabled: True
+        gpu_vendor_ids: [
+            VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+            VENDOR_ID_ARM
+        ]
+    },
+    {
+        feature_name: "globalOverrides3"
+        enabled: True
+        gpu_vendor_ids: [
+            VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+            VENDOR_ID_INTEL
+        ]
     }
 ]
 
@@ -36,5 +52,39 @@
                 enabled: True
             }
         ]
+    },
+    {
+        package_name: "com.gpuservice_unittest.packageOverrides2"
+        feature_configs: [
+            {
+                feature_name: "packageOverrides2"
+                enabled: False
+                gpu_vendor_ids: [
+                    VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+                    VENDOR_ID_INTEL
+                ]
+            }
+        ]
+    },
+    {
+        package_name: "com.gpuservice_unittest.packageOverrides3"
+        feature_configs: [
+            {
+                feature_name: "packageOverrides3_1"
+                enabled: False
+                gpu_vendor_ids: [
+                    VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+                    VENDOR_ID_ARM
+                ]
+            },
+            {
+                feature_name: "packageOverrides3_2"
+                enabled: True
+                gpu_vendor_ids: [
+                    VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+                    VENDOR_ID_INTEL
+                ]
+            }
+        ]
     }
 ]
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index a63de1c..13a089f 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,8 +1,5 @@
 # Bug component: 653544
 kocdemir@google.com
 paulthomson@google.com
-pbaiget@google.com
-lfy@google.com
 chrisforbes@google.com
-lpy@google.com
 alecmouri@google.com
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 3140dc8..98f0f34 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -19,9 +19,11 @@
 #include <android-base/logging.h>
 #include <android/configuration.h>
 #include <com_android_input_flags.h>
+#include <algorithm>
 #if defined(__ANDROID__)
 #include <gui/SurfaceComposerClient.h>
 #endif
+#include <input/InputFlags.h>
 #include <input/Keyboard.h>
 #include <input/PrintTools.h>
 #include <unordered_set>
@@ -29,42 +31,12 @@
 #include "PointerChoreographer.h"
 
 #define INDENT "  "
+#define INDENT2 "    "
 
 namespace android {
 
 namespace {
 
-bool isFromMouse(const NotifyMotionArgs& args) {
-    return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
-            args.pointerProperties[0].toolType == ToolType::MOUSE;
-}
-
-bool isFromTouchpad(const NotifyMotionArgs& args) {
-    return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
-            args.pointerProperties[0].toolType == ToolType::FINGER;
-}
-
-bool isFromDrawingTablet(const NotifyMotionArgs& args) {
-    return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
-            isStylusToolType(args.pointerProperties[0].toolType);
-}
-
-bool isHoverAction(int32_t action) {
-    return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-            action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
-}
-
-bool isStylusHoverEvent(const NotifyMotionArgs& args) {
-    return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
-}
-
-bool isMouseOrTouchpad(uint32_t sources) {
-    // Check if this is a mouse or touchpad, but not a drawing tablet.
-    return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
-            (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
-             !isFromSource(sources, AINPUT_SOURCE_STYLUS));
-}
-
 inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
                                        PointerChoreographerPolicyInterface& policy) {
     if (!change) {
@@ -161,10 +133,11 @@
         }),
         mNextListener(listener),
         mPolicy(policy),
-        mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
+        mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
         mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
         mShowTouchesEnabled(false),
         mStylusPointerIconEnabled(false),
+        mPointerMotionFilterEnabled(false),
         mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
         mIsWindowInfoListenerRegistered(false),
         mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -236,15 +209,16 @@
     PointerDisplayChange pointerDisplayChange;
     { // acquire lock
         std::scoped_lock _l(getLock());
-        if (isFromMouse(args)) {
+        if (isFromMouse(args.source, args.pointerProperties[0].toolType)) {
             newArgs = processMouseEventLocked(args);
             pointerDisplayChange = calculatePointerDisplayChangeToNotify();
-        } else if (isFromTouchpad(args)) {
+        } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) {
             newArgs = processTouchpadEventLocked(args);
             pointerDisplayChange = calculatePointerDisplayChangeToNotify();
-        } else if (isFromDrawingTablet(args)) {
+        } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) {
             processDrawingTabletEventLocked(args);
-        } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+        } else if (mStylusPointerIconEnabled &&
+                   isStylusHoverEvent(args.source, args.pointerProperties, args.action)) {
             processStylusHoverEventLocked(args);
         } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
             processTouchscreenAndStylusEventLocked(args);
@@ -322,9 +296,11 @@
                                                                  PointerControllerInterface& pc) {
     const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
     const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-
-    vec2 unconsumedDelta = pc.move(deltaX, deltaY);
-    if (com::android::input::flags::connected_displays_cursor() &&
+    vec2 filteredDelta =
+            filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
+                                                      newArgs.displayId);
+    vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
+    if (InputFlags::connectedDisplaysCursorEnabled() &&
         (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
         handleUnconsumedDeltaLocked(pc, unconsumedDelta);
         // pointer may have moved to a different viewport
@@ -386,7 +362,7 @@
         LOG(FATAL) << "A cursor already exists on destination display"
                    << destinationViewport.displayId;
     }
-    mDefaultMouseDisplayId = destinationViewport.displayId;
+    mCurrentMouseDisplayId = destinationViewport.displayId;
     auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
     pcNode.key() = destinationViewport.displayId;
     mMousePointersByDisplay.insert(std::move(pcNode));
@@ -501,6 +477,14 @@
                      << args.dump();
     }
 
+    // Fade the mouse pointer on the display if there is one when the stylus starts hovering.
+    if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+        if (const auto it = mMousePointersByDisplay.find(args.displayId);
+            it != mMousePointersByDisplay.end()) {
+            it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+        }
+    }
+
     // Get the stylus pointer controller for the device, or create one if it doesn't exist.
     auto [it, controllerAdded] =
             mStylusPointersByDevice.try_emplace(args.deviceId,
@@ -552,9 +536,6 @@
 }
 
 void PointerChoreographer::onControllerAddedOrRemovedLocked() {
-    if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
-        return;
-    }
     bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
             !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
 
@@ -619,15 +600,21 @@
 }
 
 void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
-    std::scoped_lock _l(getLock());
-    mTopology = displayTopologyGraph;
+    PointerDisplayChange pointerDisplayChange;
+    { // acquire lock
+        std::scoped_lock _l(getLock());
+        mTopology = displayTopologyGraph;
 
-    // make primary display default mouse display, if it was not set
-    // or the existing display was removed
-    if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID ||
-        mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) {
-        mDefaultMouseDisplayId = mTopology.primaryDisplayId;
-    }
+        // make primary display default mouse display, if it was not set or
+        // the existing display was removed
+        if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+            mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
+            mCurrentMouseDisplayId = mTopology.primaryDisplayId;
+            pointerDisplayChange = updatePointerControllersLocked();
+        }
+    } // release lock
+
+    notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
 }
 
 void PointerChoreographer::dump(std::string& dump) {
@@ -638,6 +625,8 @@
                          mShowTouchesEnabled ? "true" : "false");
     dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
                          mStylusPointerIconEnabled ? "true" : "false");
+    dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
+                         mPointerMotionFilterEnabled ? "true" : "false");
 
     dump += INDENT "MousePointerControllers:\n";
     for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
@@ -659,6 +648,8 @@
         std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
     }
+    dump += INDENT "DisplayTopologyGraph:\n";
+    dump += addLinePrefix(mTopology.dump(), INDENT2);
     dump += "\n";
 }
 
@@ -674,7 +665,25 @@
 
 ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
         ui::LogicalDisplayId associatedDisplayId) const {
-    return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
+    if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
+        if (associatedDisplayId.isValid()) {
+            return associatedDisplayId;
+        }
+        return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
+                                                : ui::LogicalDisplayId::DEFAULT;
+    }
+    // Associated display is not included in the topology, return this associated display.
+    if (associatedDisplayId.isValid() &&
+        mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
+        return associatedDisplayId;
+    }
+    if (mCurrentMouseDisplayId.isValid()) {
+        return mCurrentMouseDisplayId;
+    }
+    if (mTopology.primaryDisplayId.isValid()) {
+        return mTopology.primaryDisplayId;
+    }
+    return ui::LogicalDisplayId::DEFAULT;
 }
 
 std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
@@ -783,7 +792,8 @@
 PointerChoreographer::calculatePointerDisplayChangeToNotify() {
     ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
     vec2 cursorPosition = {0, 0};
-    if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+    if (const auto it =
+                mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId));
         it != mMousePointersByDisplay.end()) {
         const auto& pointerController = it->second;
         // Use the displayId from the pointerController, because it accurately reflects whether
@@ -800,12 +810,16 @@
 }
 
 void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+    if (InputFlags::connectedDisplaysCursorEnabled()) {
+        // In connected displays scenario, default mouse display will only be updated from topology.
+        return;
+    }
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
         std::scoped_lock _l(getLock());
 
-        mDefaultMouseDisplayId = displayId;
+        mCurrentMouseDisplayId = displayId;
         pointerDisplayChange = updatePointerControllersLocked();
     } // release lock
 
@@ -973,6 +987,11 @@
     mCurrentFocusedDisplay = displayId;
 }
 
+void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
+    std::scoped_lock _l(getLock());
+    mPointerMotionFilterEnabled = enabled;
+}
+
 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
         ui::LogicalDisplayId displayId) {
     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -1046,6 +1065,21 @@
     return std::nullopt;
 }
 
+vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
+        const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
+    if (!mPointerMotionFilterEnabled) {
+        return delta;
+    }
+    std::optional<vec2> filterResult =
+            mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
+    if (!filterResult.has_value()) {
+        // Disable filter when there's any error.
+        mPointerMotionFilterEnabled = false;
+        return delta;
+    }
+    return *filterResult;
+}
+
 // --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
 
 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index a9d971a..67bdca1 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -90,6 +90,11 @@
      * This method may be called on any thread (usually by the input manager on a binder thread).
      */
     virtual void dump(std::string& dump) = 0;
+
+    /**
+     * Enables motion event filter before pointer coordinates are determined.
+     */
+    virtual void setAccessibilityPointerMotionFilterEnabled(bool enabled) = 0;
 };
 
 class PointerChoreographer : public PointerChoreographerInterface {
@@ -110,6 +115,7 @@
     void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
     void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
     void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+    void setAccessibilityPointerMotionFilterEnabled(bool enabled) override;
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
@@ -168,6 +174,10 @@
                                  const DisplayTopologyPosition sourceBoundary,
                                  int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
 
+    vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta,
+                                                   const ui::LogicalDisplayId& displayId)
+            REQUIRES(getLock());
+
     /* Topology is initialized with default-constructed value, which is an empty topology. Till we
      * receive setDisplayTopology call.
      * Meanwhile Choreographer will treat every display as independent disconnected display.
@@ -221,13 +231,19 @@
     std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
             GUARDED_BY(getLock());
 
-    ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+    // In connected displays scenario, this tracks the latest display the cursor is at, within the
+    // DisplayTopology. By default, this will be set to topology primary display, and updated when
+    // mouse crossed to another display.
+    // In non-connected displays scenario, this will be treated as the default display cursor
+    // will be on, when mouse doesn't have associated display.
+    ui::LogicalDisplayId mCurrentMouseDisplayId GUARDED_BY(getLock());
     ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
     std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
     std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
     std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
     bool mShowTouchesEnabled GUARDED_BY(getLock());
     bool mStylusPointerIconEnabled GUARDED_BY(getLock());
+    bool mPointerMotionFilterEnabled GUARDED_BY(getLock());
     std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
     ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index becfb05..9cd76c7 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -125,10 +125,17 @@
     bool syntheticRepeat; // set to true for synthetic key repeats
 
     enum class InterceptKeyResult {
+        // The interception result is unknown.
         UNKNOWN,
+        // The event should be skipped and not sent to the application.
         SKIP,
+        // The event should be sent to the application.
         CONTINUE,
+        // The event should eventually be sent to the application, after a delay.
         TRY_AGAIN_LATER,
+        // The event should not be initially sent to the application, but instead go through
+        // post-processing to generate a fallback key event and then sent to the application.
+        FALLBACK,
     };
     // These are special fields that may need to be modified while the event is being dispatched.
     mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4c8147d..391703d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -32,6 +32,7 @@
 #include <gui/SurfaceComposerClient.h>
 #endif
 #include <input/InputDevice.h>
+#include <input/InputFlags.h>
 #include <input/PrintTools.h>
 #include <input/TraceTools.h>
 #include <openssl/mem.h>
@@ -933,8 +934,10 @@
         mPendingEvent(nullptr),
         mLastDropReason(DropReason::NOT_DROPPED),
         mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
+        mWindowInfosVsyncId(-1),
         mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
         mConnectionManager(mLooper),
+        mTouchStates(mWindowInfos, mConnectionManager),
         mNextUnblockedEvent(nullptr),
         mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
         mDispatchEnabled(false),
@@ -949,7 +952,7 @@
                                   new LatencyAggregatorWithHistograms()))
                         : std::move(std::unique_ptr<InputEventTimelineProcessor>(
                                   new LatencyAggregator()))),
-        mLatencyTracker(*mInputEventTimelineProcessor) {
+        mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) {
     mReporter = createInputReporter();
 
     mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
@@ -1465,14 +1468,13 @@
 
 std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets(
         ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
-        int32_t pointerId, const ConnectionManager& connections,
-        const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+        int32_t pointerId, std::function<void()> dump) {
     if (touchedWindow == nullptr) {
         return {};
     }
     // Traverse windows from front to back until we encounter the touched window.
     std::vector<InputTarget> outsideTargets;
-    const auto& windowHandles = windowInfos.getWindowHandlesForDisplay(displayId);
+    const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         if (windowHandle == touchedWindow) {
             // Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
@@ -1484,13 +1486,10 @@
         if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
             std::bitset<MAX_POINTER_ID + 1> pointerIds;
             pointerIds.set(pointerId);
-            DispatcherTouchState::addPointerWindowTarget(windowHandle,
-                                                         InputTarget::DispatchMode::OUTSIDE,
-                                                         ftl::Flags<InputTarget::Flags>(),
-                                                         pointerIds,
-                                                         /*firstDownTimeInTarget=*/std::nullopt,
-                                                         connections, windowInfos, dump,
-                                                         outsideTargets);
+            addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::OUTSIDE,
+                                   ftl::Flags<InputTarget::Flags>(), pointerIds,
+                                   /*firstDownTimeInTarget=*/std::nullopt,
+                                   /*pointerDisplayId=*/std::nullopt, dump, outsideTargets);
         }
     }
     return outsideTargets;
@@ -1962,11 +1961,74 @@
         }
     }
 
+    if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) {
+        findAndDispatchFallbackEvent(currentTime, entry, inputTargets);
+        // Drop the key.
+        return true;
+    }
+
     // Dispatch the key.
     dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
+void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime,
+                                                   std::shared_ptr<const KeyEntry> entry,
+                                                   std::vector<InputTarget>& inputTargets) {
+    // Find the fallback associated with the incoming key event and dispatch it.
+    KeyEvent event = createKeyEvent(*entry);
+    const int32_t originalKeyCode = entry->keyCode;
+
+    // Fetch the fallback event.
+    KeyCharacterMap::FallbackAction fallback;
+    for (const InputDeviceInfo& deviceInfo : mInputDevices) {
+        if (deviceInfo.getId() == entry->deviceId) {
+            const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap();
+
+            LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d",
+                                entry->deviceId);
+            map->getFallbackAction(entry->keyCode, entry->metaState, &fallback);
+            break;
+        }
+    }
+
+    if (fallback.keyCode == AKEYCODE_UNKNOWN) {
+        // No fallback detected.
+        return;
+    }
+
+    std::unique_ptr<KeyEntry> fallbackKeyEntry =
+            std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState,
+                                       event.getEventTime(), event.getDeviceId(), event.getSource(),
+                                       event.getDisplayId(), entry->policyFlags, entry->action,
+                                       event.getFlags() | AKEY_EVENT_FLAG_FALLBACK,
+                                       fallback.keyCode, event.getScanCode(), /*metaState=*/0,
+                                       event.getRepeatCount(), event.getDownTime());
+
+    if (mTracer) {
+        fallbackKeyEntry->traceTracker =
+                mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker);
+    }
+
+    for (const InputTarget& inputTarget : inputTargets) {
+        std::shared_ptr<Connection> connection = inputTarget.connection;
+        if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) {
+            return;
+        }
+
+        connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode);
+        if (entry->action == AKEY_EVENT_ACTION_UP) {
+            connection->inputState.removeFallbackKey(originalKeyCode);
+        }
+
+        if (mTracer) {
+            mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget);
+        }
+        enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection,
+                                                        std::move(fallbackKeyEntry), inputTarget);
+    }
+}
+
 void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
     LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
             << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId
@@ -2052,8 +2114,7 @@
 
         Result<std::vector<InputTarget>, InputEventInjectionResult> result =
                 mTouchStates
-                        .findTouchedWindowTargets(currentTime, *entry, mConnectionManager,
-                                                  mWindowInfos,
+                        .findTouchedWindowTargets(currentTime, *entry,
                                                   mDragState ? mDragState->dragWindow : nullptr,
                                                   std::bind_front(&InputDispatcher::
                                                                           addDragEventLocked,
@@ -2373,8 +2434,7 @@
 
 base::Result<std::vector<InputTarget>, os::InputEventInjectionResult>
 InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
-        nsecs_t currentTime, const MotionEntry& entry, const ConnectionManager& connections,
-        const DispatcherWindowInfo& windowInfos,
+        nsecs_t currentTime, const MotionEntry& entry,
         const sp<android::gui::WindowInfoHandle> dragWindow,
         std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) {
     ATRACE_CALL();
@@ -2387,12 +2447,11 @@
     const int32_t maskedAction = MotionEvent::getActionMasked(action);
 
     // Copy current touch state into tempTouchState.
-    // This state will be used to update mTouchStatesByDisplay at the end of this function.
+    // This state will be used to update saved touch state at the end of this function.
     // If no state for the specified display exists, then our initial state will be empty.
-    const TouchState* oldState = nullptr;
+    const TouchState* oldState = getTouchStateForMotionEntry(entry);
     TouchState tempTouchState;
-    if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
-        oldState = &(it->second);
+    if (oldState != nullptr) {
         tempTouchState = *oldState;
     }
 
@@ -2436,12 +2495,10 @@
         // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
         sp<WindowInfoHandle> newTouchedWindowHandle =
-                windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+                mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
 
         if (isDown) {
-            targets += DispatcherTouchState::findOutsideTargets(displayId, newTouchedWindowHandle,
-                                                                pointer.id, connections,
-                                                                windowInfos, dump);
+            targets += findOutsideTargets(displayId, newTouchedWindowHandle, pointer.id, dump);
         }
         LOG_IF(INFO, newTouchedWindowHandle == nullptr)
                 << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
@@ -2454,7 +2511,7 @@
         }
 
         std::vector<sp<WindowInfoHandle>> newTouchedWindows =
-                findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, windowInfos);
+                findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos);
         if (newTouchedWindowHandle != nullptr) {
             // Process the foreground window first so that it is the first to receive the event.
             newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2467,7 +2524,7 @@
         }
 
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
-            if (!canWindowReceiveMotion(windowHandle, entry, connections, windowInfos)) {
+            if (!canWindowReceiveMotion(windowHandle, entry)) {
                 continue;
             }
 
@@ -2479,8 +2536,7 @@
 
             // Set target flags.
             ftl::Flags<InputTarget::Flags> targetFlags =
-                    DispatcherTouchState::getTargetFlags(windowHandle, {x, y}, isSplit,
-                                                         windowInfos);
+                    getTargetFlags(windowHandle, {x, y}, isSplit);
 
             // Update the temporary touch state.
 
@@ -2510,7 +2566,7 @@
                     windowHandle->getInfo()->inputConfig.test(
                             gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                     sp<WindowInfoHandle> wallpaper =
-                            windowInfos.findWallpaperWindowBelow(windowHandle);
+                            mWindowInfos.findWallpaperWindowBelow(windowHandle);
                     if (wallpaper != nullptr) {
                         ftl::Flags<InputTarget::Flags> wallpaperFlags =
                                 InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2579,7 +2635,7 @@
                     tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
             LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
             sp<WindowInfoHandle> newTouchedWindowHandle =
-                    windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+                    mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
 
             // Verify targeted injection.
             if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2589,7 +2645,7 @@
 
             // Do not slide events to the window which can not receive motion event
             if (newTouchedWindowHandle != nullptr &&
-                !canWindowReceiveMotion(newTouchedWindowHandle, entry, connections, windowInfos)) {
+                !canWindowReceiveMotion(newTouchedWindowHandle, entry)) {
                 newTouchedWindowHandle = nullptr;
             }
 
@@ -2606,18 +2662,16 @@
 
                 const TouchedWindow& touchedWindow =
                         tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
-                DispatcherTouchState::
-                        addPointerWindowTarget(oldTouchedWindowHandle,
-                                               InputTarget::DispatchMode::SLIPPERY_EXIT,
-                                               ftl::Flags<InputTarget::Flags>(), pointerIds,
-                                               touchedWindow.getDownTimeInTarget(entry.deviceId),
-                                               connections, windowInfos, dump, targets);
+                addPointerWindowTarget(oldTouchedWindowHandle,
+                                       InputTarget::DispatchMode::SLIPPERY_EXIT,
+                                       ftl::Flags<InputTarget::Flags>(), pointerIds,
+                                       touchedWindow.getDownTimeInTarget(entry.deviceId),
+                                       /*pointerDisplayId=*/std::nullopt, dump, targets);
 
                 // Make a slippery entrance into the new window.
 
                 ftl::Flags<InputTarget::Flags> targetFlags =
-                        DispatcherTouchState::getTargetFlags(newTouchedWindowHandle, {x, y},
-                                                             isSplit, windowInfos);
+                        getTargetFlags(newTouchedWindowHandle, {x, y}, isSplit);
 
                 tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
                                                  InputTarget::DispatchMode::SLIPPERY_ENTER,
@@ -2625,10 +2679,8 @@
                                                  entry.eventTime);
 
                 // Check if the wallpaper window should deliver the corresponding event.
-                DispatcherTouchState::slipWallpaperTouch(targetFlags, oldTouchedWindowHandle,
-                                                         newTouchedWindowHandle, tempTouchState,
-                                                         entry, targets, connections, windowInfos,
-                                                         dump);
+                slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
+                                   tempTouchState, entry, targets, dump);
                 tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
                                                                oldTouchedWindowHandle);
             }
@@ -2660,12 +2712,10 @@
         std::bitset<MAX_POINTER_ID + 1> pointerIds;
         pointerIds.set(entry.pointerProperties[0].id);
         for (const TouchedWindow& touchedWindow : hoveringWindows) {
-            DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
-                                                         touchedWindow.dispatchMode,
-                                                         touchedWindow.targetFlags, pointerIds,
-                                                         touchedWindow.getDownTimeInTarget(
-                                                                 entry.deviceId),
-                                                         connections, windowInfos, dump, targets);
+            addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+                                   touchedWindow.targetFlags, pointerIds,
+                                   touchedWindow.getDownTimeInTarget(entry.deviceId),
+                                   /*pointerDisplayId=*/std::nullopt, dump, targets);
         }
     }
 
@@ -2694,7 +2744,7 @@
             for (InputTarget& target : targets) {
                 if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
                     sp<WindowInfoHandle> targetWindow =
-                            windowInfos.findWindowHandle(target.connection->getToken());
+                            mWindowInfos.findWindowHandle(target.connection->getToken());
                     if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
                         target.flags |= InputTarget::Flags::ZERO_COORDS;
                     }
@@ -2718,13 +2768,10 @@
         if (touchingPointers.empty()) {
             continue;
         }
-        DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
-                                                     touchedWindow.dispatchMode,
-                                                     touchedWindow.targetFlags,
-                                                     getPointerIds(touchingPointers),
-                                                     touchedWindow.getDownTimeInTarget(
-                                                             entry.deviceId),
-                                                     connections, windowInfos, dump, targets);
+        addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+                               touchedWindow.targetFlags, getPointerIds(touchingPointers),
+                               touchedWindow.getDownTimeInTarget(entry.deviceId),
+                               /*pointerDisplayId=*/displayId, dump, targets);
     }
 
     // During targeted injection, only allow owned targets to receive events
@@ -2779,16 +2826,12 @@
     if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
         if (displayId >= ui::LogicalDisplayId::DEFAULT) {
             tempTouchState.clearWindowsWithoutPointers();
-            mTouchStatesByDisplay[displayId] = tempTouchState;
+            saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
         } else {
-            mTouchStatesByDisplay.erase(displayId);
+            eraseTouchStateForMotionEntry(entry);
         }
     }
 
-    if (tempTouchState.windows.empty()) {
-        mTouchStatesByDisplay.erase(displayId);
-    }
-
     return targets;
 }
 
@@ -2811,8 +2854,9 @@
 }
 
 void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
-    if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
-        mDragState->deviceId != entry.deviceId) {
+    if (!mDragState || mDragState->deviceId != entry.deviceId ||
+        !mWindowInfos.areDisplaysConnected(mDragState->dragWindow->getInfo()->displayId,
+                                           entry.displayId)) {
         return;
     }
 
@@ -2931,8 +2975,8 @@
         const sp<android::gui::WindowInfoHandle>& windowHandle,
         InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
         std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget,
-        const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
-        std::function<void()> dump, std::vector<InputTarget>& inputTargets) {
+        std::optional<ui::LogicalDisplayId> pointerDisplayId, std::function<void()> dump,
+        std::vector<InputTarget>& inputTargets) {
     if (pointerIds.none()) {
         for (const auto& target : inputTargets) {
             LOG(INFO) << "Target: " << target;
@@ -2960,14 +3004,15 @@
     const WindowInfo& windowInfo = *windowHandle->getInfo();
 
     if (it == inputTargets.end()) {
-        std::shared_ptr<Connection> connection = connections.getConnection(windowInfo.token);
+        std::shared_ptr<Connection> connection = mConnectionManager.getConnection(windowInfo.token);
         if (connection == nullptr) {
             ALOGW("Not creating InputTarget for %s, no input channel", windowInfo.name.c_str());
             return;
         }
         inputTargets.push_back(
                 createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
-                                  windowInfos.getRawTransform(*windowHandle->getInfo()),
+                                  mWindowInfos.getRawTransform(*windowHandle->getInfo(),
+                                                               pointerDisplayId),
                                   firstDownTimeInTarget));
         it = inputTargets.end() - 1;
     }
@@ -4167,15 +4212,15 @@
                         sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
                         mDragState.reset();
                     }
-                    DispatcherTouchState::
-                            addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
-                                                   ftl::Flags<InputTarget::Flags>(), pointerIds,
-                                                   motionEntry.downTime, mConnectionManager,
-                                                   mWindowInfos,
-                                                   std::bind_front(&InputDispatcher::
-                                                                           logDispatchStateLocked,
-                                                                   this),
-                                                   targets);
+                    mTouchStates
+                            .addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
+                                                    ftl::Flags<InputTarget::Flags>(), pointerIds,
+                                                    motionEntry.downTime,
+                                                    /*pointerDisplayId=*/std::nullopt,
+                                                    std::bind_front(&InputDispatcher::
+                                                                            logDispatchStateLocked,
+                                                                    this),
+                                                    targets);
                 } else {
                     targets.emplace_back(fallbackTarget);
                     // Since we don't have a window, use the display transform as the raw transform.
@@ -4235,13 +4280,8 @@
             << "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
             << downEvents.size() << " down events to ensure consistent event stream.";
 
-    auto touchedWindowHandleAndDisplay =
-            mTouchStates.findTouchedWindowHandleAndDisplay(connection->getToken());
-    if (!touchedWindowHandleAndDisplay.has_value()) {
-        LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
-    }
-
-    const auto [windowHandle, displayId] = touchedWindowHandleAndDisplay.value();
+    const auto [windowHandle, displayId] =
+            mTouchStates.findExistingTouchedWindowHandleAndDisplay(connection->getToken());
 
     const bool wasEmpty = connection->outboundQueue.empty();
     for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
@@ -4259,14 +4299,14 @@
                          pointerIndex++) {
                         pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
                     }
-                    DispatcherTouchState::
-                            addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
-                                                   targetFlags, pointerIds, motionEntry.downTime,
-                                                   mConnectionManager, mWindowInfos,
-                                                   std::bind_front(&InputDispatcher::
-                                                                           logDispatchStateLocked,
-                                                                   this),
-                                                   targets);
+                    mTouchStates
+                            .addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
+                                                    targetFlags, pointerIds, motionEntry.downTime,
+                                                    /*pointerDisplayId=*/std::nullopt,
+                                                    std::bind_front(&InputDispatcher::
+                                                                            logDispatchStateLocked,
+                                                                    this),
+                                                    targets);
                 } else {
                     targets.emplace_back(connection, targetFlags);
                     // Since we don't have a window, use the display transform as the raw transform.
@@ -4369,7 +4409,7 @@
     std::scoped_lock _l(mLock);
     // Reset key repeating in case a keyboard device was added or removed or something.
     resetKeyRepeatLocked();
-    mLatencyTracker.setInputDevices(args.inputDeviceInfos);
+    mInputDevices = args.inputDeviceInfos;
 }
 
 void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
@@ -5091,8 +5131,19 @@
     }
 
     // Only look through the requested display.
-    for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) {
-        if (windowHandle->getToken() == windowHandleToken) {
+    return findWindowHandleOnDisplay(windowHandleToken, *displayId);
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays(
+        const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+    if (windowHandleToken == nullptr) {
+        return nullptr;
+    }
+
+    sp<WindowInfoHandle> windowHandle;
+    for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) {
+        windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId);
+        if (windowHandle != nullptr) {
             return windowHandle;
         }
     }
@@ -5174,13 +5225,38 @@
 }
 
 ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
-        const android::gui::WindowInfo& windowInfo) const {
+        const android::gui::WindowInfo& windowInfo,
+        std::optional<ui::LogicalDisplayId> pointerDisplayId) const {
+    // TODO(b/383092013): Handle TOPOLOGY_AWARE window flag.
+    // For now, we assume all windows are topology-aware and can handle cross-display streams.
+    if (com::android::input::flags::connected_displays_cursor() && pointerDisplayId.has_value() &&
+        *pointerDisplayId != windowInfo.displayId) {
+        // Sending pointer to a different display than the window. This is a
+        // cross-display drag gesture, so always use the new display's transform.
+        return getDisplayTransform(*pointerDisplayId);
+    }
     // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
-    // APIs. If not, fall back to using the DisplayInfo transform of the window's display.
-    return (input_flags::use_cloned_screen_coordinates_as_raw() &&
-            windowInfo.cloneLayerStackTransform)
-            ? *windowInfo.cloneLayerStackTransform
-            : getDisplayTransform(windowInfo.displayId);
+    // APIs. If not, fall back to using the DisplayInfo transform of the window's display
+    bool useClonedScreenCoordinates = (input_flags::use_cloned_screen_coordinates_as_raw() &&
+                                       windowInfo.cloneLayerStackTransform);
+    if (useClonedScreenCoordinates) {
+        return *windowInfo.cloneLayerStackTransform;
+    }
+    return getDisplayTransform(windowInfo.displayId);
+}
+
+ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId(
+        ui::LogicalDisplayId displayId) const {
+    if (mTopology.graph.contains(displayId)) {
+        return mTopology.primaryDisplayId;
+    }
+    return displayId;
+}
+
+bool InputDispatcher::DispatcherWindowInfo::areDisplaysConnected(
+        ui::LogicalDisplayId display1, ui::LogicalDisplayId display2) const {
+    return display1 == display2 ||
+            (mTopology.graph.contains(display1) && mTopology.graph.contains(display2));
 }
 
 std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
@@ -5210,13 +5286,40 @@
     } else {
         dump += "Displays: <none>\n";
     }
+    dump += StringPrintf("mMaximumObscuringOpacityForTouch: %f\n",
+                         mMaximumObscuringOpacityForTouch);
+    dump += "DisplayTopologyGraph:\n";
+    dump += addLinePrefix(mTopology.dump(), INDENT);
+    dump += "\n";
     return dump;
 }
 
+std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays(
+        ui::LogicalDisplayId displayId) const {
+    if (!mTopology.graph.contains(displayId)) {
+        return {displayId};
+    }
+
+    std::vector<ui::LogicalDisplayId> connectedDisplays;
+    for (auto it : mTopology.graph) {
+        connectedDisplays.push_back(it.first);
+    }
+    return connectedDisplays;
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay(
+        const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+    for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) {
+        if (windowHandle->getToken() == windowHandleToken) {
+            return windowHandle;
+        }
+    }
+    return nullptr;
+}
+
 bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion(
         const sp<android::gui::WindowInfoHandle>& window,
-        const android::inputdispatcher::MotionEntry& motionEntry,
-        const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos) const {
+        const android::inputdispatcher::MotionEntry& motionEntry) const {
     const WindowInfo& info = *window->getInfo();
 
     // Skip spy window targets that are not valid for targeted injection.
@@ -5235,7 +5338,7 @@
         return false;
     }
 
-    std::shared_ptr<Connection> connection = connections.getConnection(window->getToken());
+    std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken());
     if (connection == nullptr) {
         ALOGW("Not sending touch to %s because there's no corresponding connection",
               window->getName().c_str());
@@ -5250,8 +5353,8 @@
     // Drop events that can't be trusted due to occlusion
     const auto [x, y] = resolveTouchedPosition(motionEntry);
     DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
-            windowInfos.computeTouchOcclusionInfo(window, x, y);
-    if (!windowInfos.isTouchTrusted(occlusionInfo)) {
+            mWindowInfos.computeTouchOcclusionInfo(window, x, y);
+    if (!mWindowInfos.isTouchTrusted(occlusionInfo)) {
         if (DEBUG_TOUCH_OCCLUSION) {
             ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
             for (const auto& log : occlusionInfo.debugInfo) {
@@ -5264,7 +5367,7 @@
     }
 
     // Drop touch events if requested by input feature
-    if (shouldDropInput(motionEntry, window, windowInfos)) {
+    if (shouldDropInput(motionEntry, window, mWindowInfos)) {
         return false;
     }
 
@@ -5400,7 +5503,7 @@
     CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
                                                 "WindowInfo changed", traceContext.getTracker());
     const std::list<DispatcherTouchState::CancellationArgs> cancellations =
-            mTouchStates.updateFromWindowInfo(displayId, mWindowInfos);
+            mTouchStates.updateFromWindowInfo(displayId);
     for (const auto& cancellationArgs : cancellations) {
         switch (cancellationArgs.mode) {
             case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
@@ -5441,26 +5544,25 @@
 }
 
 std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
-InputDispatcher::DispatcherTouchState::updateFromWindowInfo(
-        ui::LogicalDisplayId displayId, const DispatcherWindowInfo& windowInfos) {
+InputDispatcher::DispatcherTouchState::updateFromWindowInfo(ui::LogicalDisplayId displayId) {
     std::list<CancellationArgs> cancellations;
-    if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
-        TouchState& state = it->second;
-        cancellations = eraseRemovedWindowsFromWindowInfo(state, displayId, windowInfos);
+    forTouchAndCursorStatesOnDisplay(displayId, [&](TouchState& state) {
         cancellations.splice(cancellations.end(),
-                             updateHoveringStateFromWindowInfo(state, displayId, windowInfos));
-    }
+                             eraseRemovedWindowsFromWindowInfo(state, displayId));
+        cancellations.splice(cancellations.end(),
+                             updateHoveringStateFromWindowInfo(state, displayId));
+        return false;
+    });
     return cancellations;
 }
 
 std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
 InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo(
-        TouchState& state, ui::LogicalDisplayId displayId,
-        const DispatcherWindowInfo& windowInfos) {
+        TouchState& state, ui::LogicalDisplayId displayId) {
     std::list<CancellationArgs> cancellations;
     for (auto it = state.windows.begin(); it != state.windows.end();) {
         TouchedWindow& touchedWindow = *it;
-        if (windowInfos.isWindowPresent(touchedWindow.windowHandle)) {
+        if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
             it++;
             continue;
         }
@@ -5486,12 +5588,11 @@
 
 std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
 InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo(
-        TouchState& state, ui::LogicalDisplayId displayId,
-        const DispatcherWindowInfo& windowInfos) {
+        TouchState& state, ui::LogicalDisplayId displayId) {
     std::list<CancellationArgs> cancellations;
     // Check if the hovering should stop because the window is no longer eligible to receive it
     // (for example, if the touchable region changed)
-    ui::Transform displayTransform = windowInfos.getDisplayTransform(displayId);
+    ui::Transform displayTransform = mWindowInfos.getDisplayTransform(displayId);
     for (TouchedWindow& touchedWindow : state.windows) {
         std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
                 [&](const PointerProperties& properties, float x, float y) {
@@ -5746,8 +5847,7 @@
                                    "transferring touch from this window to another window",
                                    traceContext.getTracker());
 
-        auto result = mTouchStates.transferTouchGesture(fromToken, toToken, mWindowInfos,
-                                                        mConnectionManager);
+        auto result = mTouchStates.transferTouchGesture(fromToken, toToken);
         if (!result.has_value()) {
             return false;
         }
@@ -5791,9 +5891,7 @@
                          std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
                          std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>>
 InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken,
-                                                            const sp<android::IBinder>& toToken,
-                                                            const DispatcherWindowInfo& windowInfos,
-                                                            const ConnectionManager& connections) {
+                                                            const sp<android::IBinder>& toToken) {
     // Find the target touch state and touched window by fromToken.
     auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken);
     if (!touchStateWindowAndDisplay.has_value()) {
@@ -5811,7 +5909,10 @@
     const DeviceId deviceId = *deviceIds.begin();
 
     const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle;
-    const sp<WindowInfoHandle> toWindowHandle = windowInfos.findWindowHandle(toToken, displayId);
+    // TouchState displayId may not be same as window displayId, we need to lookup for toToken on
+    // all connected displays.
+    const sp<WindowInfoHandle> toWindowHandle =
+            mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId);
     if (!toWindowHandle) {
         ALOGW("Cannot transfer touch because the transfer target window was not found.");
         return std::nullopt;
@@ -5837,8 +5938,8 @@
                             deviceId, pointers, downTimeInTarget);
 
     // Synthesize cancel for old window and down for new window.
-    std::shared_ptr<Connection> fromConnection = connections.getConnection(fromToken);
-    std::shared_ptr<Connection> toConnection = connections.getConnection(toToken);
+    std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken);
+    std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken);
     std::list<CancellationArgs> cancellations;
     std::list<PointerDownArgs> pointerDowns;
     if (fromConnection != nullptr && toConnection != nullptr) {
@@ -5849,7 +5950,7 @@
         // Check if the wallpaper window should deliver the corresponding event.
         auto [wallpaperCancellations, wallpaperPointerDowns] =
                 transferWallpaperTouch(fromWindowHandle, toWindowHandle, state, deviceId, pointers,
-                                       oldTargetFlags, newTargetFlags, windowInfos, connections);
+                                       oldTargetFlags, newTargetFlags);
 
         cancellations.splice(cancellations.end(), wallpaperCancellations);
         pointerDowns.splice(pointerDowns.end(), wallpaperPointerDowns);
@@ -5871,26 +5972,25 @@
  */
 sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow(
         ui::LogicalDisplayId displayId) const {
-    const auto stateIt = mTouchStatesByDisplay.find(displayId);
-    if (stateIt == mTouchStatesByDisplay.end()) {
-        ALOGI("No touch state on display %s", displayId.toString().c_str());
-        return nullptr;
-    }
-
-    const TouchState& state = stateIt->second;
     sp<WindowInfoHandle> touchedForegroundWindow;
-    // If multiple foreground windows are touched, return nullptr
-    for (const TouchedWindow& window : state.windows) {
-        if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
-            if (touchedForegroundWindow != nullptr) {
-                ALOGI("Two or more foreground windows: %s and %s",
-                      touchedForegroundWindow->getName().c_str(),
-                      window.windowHandle->getName().c_str());
-                return nullptr;
+    forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+        // If multiple foreground windows are touched, return nullptr
+        for (const TouchedWindow& window : state.windows) {
+            if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
+                if (touchedForegroundWindow != nullptr) {
+                    ALOGI("Two or more foreground windows: %s and %s",
+                          touchedForegroundWindow->getName().c_str(),
+                          window.windowHandle->getName().c_str());
+                    touchedForegroundWindow = nullptr;
+                    return true;
+                }
+                touchedForegroundWindow = window.windowHandle;
             }
-            touchedForegroundWindow = window.windowHandle;
         }
-    }
+        return false;
+    });
+    ALOGI_IF(touchedForegroundWindow == nullptr,
+             "No touch state or no touched foreground window on display %d", displayId.val());
     return touchedForegroundWindow;
 }
 
@@ -7013,11 +7113,6 @@
             setInputWindowsLocked(handles, displayId);
         }
 
-        if (update.vsyncId < mWindowInfosVsyncId) {
-            ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
-                  ", current update vsync id: %" PRId64,
-                  mWindowInfosVsyncId, update.vsyncId);
-        }
         mWindowInfosVsyncId = update.vsyncId;
     }
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -7069,8 +7164,7 @@
 void InputDispatcher::DispatcherTouchState::slipWallpaperTouch(
         ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle,
         const sp<WindowInfoHandle>& newWindowHandle, TouchState& state, const MotionEntry& entry,
-        std::vector<InputTarget>& targets, const ConnectionManager& connections,
-        const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+        std::vector<InputTarget>& targets, std::function<void()> dump) {
     LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
     const DeviceId deviceId = entry.deviceId;
     const PointerProperties& pointerProperties = entry.pointerProperties[0];
@@ -7083,19 +7177,17 @@
     const sp<WindowInfoHandle> oldWallpaper =
             oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
     const sp<WindowInfoHandle> newWallpaper =
-            newHasWallpaper ? windowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
+            newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
     if (oldWallpaper == newWallpaper) {
         return;
     }
 
     if (oldWallpaper != nullptr) {
         const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
-        DispatcherTouchState::addPointerWindowTarget(oldWallpaper,
-                                                     InputTarget::DispatchMode::SLIPPERY_EXIT,
-                                                     oldTouchedWindow.targetFlags,
-                                                     getPointerIds(pointers),
-                                                     oldTouchedWindow.getDownTimeInTarget(deviceId),
-                                                     connections, windowInfos, dump, targets);
+        addPointerWindowTarget(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
+                               oldTouchedWindow.targetFlags, getPointerIds(pointers),
+                               oldTouchedWindow.getDownTimeInTarget(deviceId),
+                               /*pointerDisplayId=*/std::nullopt, dump, targets);
         state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
     }
 
@@ -7114,8 +7206,7 @@
         const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
         android::DeviceId deviceId, const std::vector<PointerProperties>& pointers,
         ftl::Flags<InputTarget::Flags> oldTargetFlags,
-        ftl::Flags<InputTarget::Flags> newTargetFlags, const DispatcherWindowInfo& windowInfos,
-        const ConnectionManager& connections) {
+        ftl::Flags<InputTarget::Flags> newTargetFlags) {
     const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
             fromWindowHandle->getInfo()->inputConfig.test(
                     gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7126,7 +7217,7 @@
     const sp<WindowInfoHandle> oldWallpaper =
             oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
     const sp<WindowInfoHandle> newWallpaper =
-            newHasWallpaper ? windowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
+            newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
     if (oldWallpaper == newWallpaper) {
         return {};
     }
@@ -7147,10 +7238,10 @@
         state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
                                 deviceId, pointers, downTimeInTarget);
         std::shared_ptr<Connection> wallpaperConnection =
-                connections.getConnection(newWallpaper->getToken());
+                mConnectionManager.getConnection(newWallpaper->getToken());
         if (wallpaperConnection != nullptr) {
             std::shared_ptr<Connection> toConnection =
-                    connections.getConnection(toWindowHandle->getToken());
+                    mConnectionManager.getConnection(toWindowHandle->getToken());
             toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
             pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags);
         }
@@ -7350,9 +7441,12 @@
     mTopology = displayTopologyGraph;
 }
 
+InputDispatcher::DispatcherTouchState::DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+                                                            const ConnectionManager& connections)
+      : mWindowInfos(windowInfos), mConnectionManager(connections) {}
+
 ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags(
-        const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit,
-        const DispatcherWindowInfo& windowInfos) {
+        const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit) {
     ftl::Flags<InputTarget::Flags> targetFlags;
     if (canReceiveForegroundTouches(*targetWindow->getInfo())) {
         // There should only be one touched window that can be "foreground" for the pointer.
@@ -7361,9 +7455,9 @@
     if (isSplit) {
         targetFlags |= InputTarget::Flags::SPLIT;
     }
-    if (windowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
+    if (mWindowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
         targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
-    } else if (windowInfos.isWindowObscured(targetWindow)) {
+    } else if (mWindowInfos.isWindowObscured(targetWindow)) {
         targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
     }
     return targetFlags;
@@ -7371,101 +7465,212 @@
 
 bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers(
         ui::LogicalDisplayId displayId, int32_t deviceId) const {
-    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-    if (touchStateIt == mTouchStatesByDisplay.end()) {
-        return false;
-    }
-    return touchStateIt->second.hasTouchingPointers(deviceId) ||
-            touchStateIt->second.hasHoveringPointers(deviceId);
+    bool hasTouchingOrHoveringPointers = false;
+    forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+        hasTouchingOrHoveringPointers =
+                state.hasTouchingPointers(deviceId) || state.hasHoveringPointers(deviceId);
+        return hasTouchingOrHoveringPointers;
+    });
+    return hasTouchingOrHoveringPointers;
 }
 
 bool InputDispatcher::DispatcherTouchState::isPointerInWindow(const sp<android::IBinder>& token,
                                                               ui::LogicalDisplayId displayId,
                                                               android::DeviceId deviceId,
                                                               int32_t pointerId) const {
-    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-    if (touchStateIt == mTouchStatesByDisplay.end()) {
-        return false;
-    }
-    for (const TouchedWindow& window : touchStateIt->second.windows) {
-        if (window.windowHandle->getToken() == token &&
-            (window.hasTouchingPointer(deviceId, pointerId) ||
-             window.hasHoveringPointer(deviceId, pointerId))) {
-            return true;
-        }
-    }
-    return false;
-}
-
-std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
-InputDispatcher::DispatcherTouchState::findTouchedWindowHandleAndDisplay(
-        const sp<android::IBinder>& token) const {
-    for (const auto& [displayId, state] : mTouchStatesByDisplay) {
-        for (const TouchedWindow& w : state.windows) {
-            if (w.windowHandle->getToken() == token) {
-                return std::make_tuple(std::ref(w.windowHandle), displayId);
+    bool isPointerInWindow = false;
+    forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+        for (const TouchedWindow& window : state.windows) {
+            if (window.windowHandle->getToken() == token &&
+                (window.hasTouchingPointer(deviceId, pointerId) ||
+                 window.hasHoveringPointer(deviceId, pointerId))) {
+                isPointerInWindow = true;
+                return true;
             }
         }
-    }
-    return std::nullopt;
+        return false;
+    });
+    return isPointerInWindow;
+}
+
+std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+InputDispatcher::DispatcherTouchState::findExistingTouchedWindowHandleAndDisplay(
+        const sp<android::IBinder>& token) const {
+    std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
+            touchedWindowHandleAndDisplay;
+    forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
+        for (const TouchedWindow& w : state.windows) {
+            if (w.windowHandle->getToken() == token) {
+                touchedWindowHandleAndDisplay.emplace(std::ref(w.windowHandle), displayId);
+                return true;
+            }
+        }
+        return false;
+    });
+    LOG_ALWAYS_FATAL_IF(!touchedWindowHandleAndDisplay.has_value(),
+                        "%s : Touch state is out of sync: No touched window for token", __func__);
+    return touchedWindowHandleAndDisplay.value();
 }
 
 void InputDispatcher::DispatcherTouchState::forAllTouchedWindows(
         std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
-    for (const auto& [_, state] : mTouchStatesByDisplay) {
+    forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
         for (const TouchedWindow& window : state.windows) {
             f(window.windowHandle);
         }
-    }
+        return false;
+    });
 }
 
 void InputDispatcher::DispatcherTouchState::forAllTouchedWindowsOnDisplay(
         ui::LogicalDisplayId displayId,
         std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
-    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-    if (touchStateIt == mTouchStatesByDisplay.end()) {
-        return;
-    }
-    for (const TouchedWindow& window : touchStateIt->second.windows) {
-        f(window.windowHandle);
-    }
+    forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+        for (const TouchedWindow& window : state.windows) {
+            f(window.windowHandle);
+        }
+        return false;
+    });
 }
 
 std::string InputDispatcher::DispatcherTouchState::dump() const {
     std::string dump;
-    if (!mTouchStatesByDisplay.empty()) {
-        dump += StringPrintf("TouchStatesByDisplay:\n");
+    if (mTouchStatesByDisplay.empty()) {
+        dump += "TouchStatesByDisplay: <no displays touched>\n";
+    } else {
+        dump += "TouchStatesByDisplay:\n";
         for (const auto& [displayId, state] : mTouchStatesByDisplay) {
             std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
             dump += INDENT + displayId.toString() + " : " + touchStateDump;
         }
+    }
+    if (mCursorStateByDisplay.empty()) {
+        dump += "CursorStatesByDisplay: <no displays touched by cursor>\n";
     } else {
-        dump += "TouchStates: <no displays touched>\n";
+        dump += "CursorStatesByDisplay:\n";
+        for (const auto& [displayId, state] : mCursorStateByDisplay) {
+            std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
+            dump += INDENT + displayId.toString() + " : " + touchStateDump;
+        }
     }
     return dump;
 }
 
 void InputDispatcher::DispatcherTouchState::removeAllPointersForDevice(android::DeviceId deviceId) {
-    for (auto& [_, touchState] : mTouchStatesByDisplay) {
-        touchState.removeAllPointersForDevice(deviceId);
-    }
+    forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
+        state.removeAllPointersForDevice(deviceId);
+        return false;
+    });
 }
 
 void InputDispatcher::DispatcherTouchState::clear() {
     mTouchStatesByDisplay.clear();
+    mCursorStateByDisplay.clear();
+}
+
+void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
+        const android::inputdispatcher::MotionEntry& entry,
+        android::inputdispatcher::TouchState&& touchState) {
+    if (touchState.windows.empty()) {
+        eraseTouchStateForMotionEntry(entry);
+        return;
+    }
+
+    if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+        mCursorStateByDisplay[mWindowInfos.getPrimaryDisplayId(entry.displayId)] =
+                std::move(touchState);
+    } else {
+        mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
+    }
+}
+
+void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
+        const android::inputdispatcher::MotionEntry& entry) {
+    if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+        mCursorStateByDisplay.erase(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+    } else {
+        mTouchStatesByDisplay.erase(entry.displayId);
+    }
+}
+
+const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry(
+        const android::inputdispatcher::MotionEntry& entry) const {
+    if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+        auto touchStateIt =
+                mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+        if (touchStateIt != mCursorStateByDisplay.end()) {
+            return &touchStateIt->second;
+        }
+    } else {
+        auto touchStateIt = mTouchStatesByDisplay.find(entry.displayId);
+        if (touchStateIt != mTouchStatesByDisplay.end()) {
+            return &touchStateIt->second;
+        }
+    }
+    return nullptr;
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+        ui::LogicalDisplayId displayId, std::function<bool(const TouchState&)> f) const {
+    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+    if (touchStateIt != mTouchStatesByDisplay.end() && f(touchStateIt->second)) {
+        return;
+    }
+
+    // DisplayId for the Cursor state may not be same as supplied displayId if display is part of
+    // topology. Instead we should to check from the topology's primary display.
+    const auto cursorStateIt =
+            mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(displayId));
+    if (cursorStateIt != mCursorStateByDisplay.end()) {
+        f(cursorStateIt->second);
+    }
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+        ui::LogicalDisplayId displayId, std::function<bool(TouchState&)> f) {
+    const_cast<const DispatcherTouchState&>(*this)
+            .forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+                return f(const_cast<TouchState&>(state));
+            });
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+        std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const {
+    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+        if (f(displayId, state)) {
+            return;
+        }
+    }
+    for (auto& [displayId, state] : mCursorStateByDisplay) {
+        if (f(displayId, state)) {
+            return;
+        }
+    }
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+        std::function<bool(ui::LogicalDisplayId, TouchState&)> f) {
+    const_cast<const DispatcherTouchState&>(*this).forAllTouchAndCursorStates(
+            [&](ui::LogicalDisplayId displayId, const TouchState& constState) {
+                return f(displayId, const_cast<TouchState&>(constState));
+            });
 }
 
 std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
 InputDispatcher::DispatcherTouchState::findTouchStateWindowAndDisplay(
         const sp<android::IBinder>& token) {
-    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+    std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
+            touchStateWindowAndDisplay;
+    forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
         for (TouchedWindow& w : state.windows) {
             if (w.windowHandle->getToken() == token) {
-                return std::make_tuple(std::ref(state), std::ref(w), displayId);
+                touchStateWindowAndDisplay.emplace(std::ref(state), std::ref(w), displayId);
+                return true;
             }
         }
-    }
-    return std::nullopt;
+        return false;
+    });
+    return touchStateWindowAndDisplay;
 }
 
 bool InputDispatcher::DispatcherTouchState::isStylusActiveInDisplay(
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e76bd89..ad7e87e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -307,8 +307,11 @@
         // Get the transform for display, returns Identity-transform if display is missing.
         ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
 
-        // Get the raw transform to use for motion events going to the given window.
-        ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+        // Get the raw transform to use for motion events going to the given window. Optionally a
+        // pointer displayId may be supplied if pointer is on a different display from the window.
+        ui::Transform getRawTransform(
+                const android::gui::WindowInfo& windowInfo,
+                std::optional<ui::LogicalDisplayId> pointerDisplayId = std::nullopt) const;
 
         // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
         // display-id is not provided lookup is done for all displays.
@@ -316,6 +319,11 @@
                 const sp<IBinder>& windowHandleToken,
                 std::optional<ui::LogicalDisplayId> displayId = {}) const;
 
+        // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected
+        // displays in the topology of the queried display.
+        sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays(
+                const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
         bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
 
         // Returns the touched window at the given location, excluding the ignoreWindow if provided.
@@ -336,9 +344,22 @@
 
         bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const;
 
+        // Returns topology's primary display if the display belongs to it, otherwise the
+        // same displayId.
+        ui::LogicalDisplayId getPrimaryDisplayId(ui::LogicalDisplayId displayId) const;
+
+        bool areDisplaysConnected(ui::LogicalDisplayId display1,
+                                  ui::LogicalDisplayId display2) const;
+
         std::string dumpDisplayAndWindowInfo() const;
 
     private:
+        std::vector<ui::LogicalDisplayId> getConnectedDisplays(
+                ui::LogicalDisplayId displayId) const;
+
+        sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay(
+                const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
         std::unordered_map<ui::LogicalDisplayId /*displayId*/,
                            std::vector<sp<android::gui::WindowInfoHandle>>>
                 mWindowHandlesByDisplay;
@@ -370,20 +391,20 @@
             const ftl::Flags<InputTarget::Flags> targetFlags;
         };
 
-        static void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                           InputTarget::DispatchMode dispatchMode,
-                                           ftl::Flags<InputTarget::Flags> targetFlags,
-                                           std::bitset<MAX_POINTER_ID + 1> pointerIds,
-                                           std::optional<nsecs_t> firstDownTimeInTarget,
-                                           const ConnectionManager& connections,
-                                           const DispatcherWindowInfo& windowInfos,
-                                           std::function<void()> dump,
-                                           std::vector<InputTarget>& inputTargets);
+        DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+                             const ConnectionManager& connections);
+
+        void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                                    InputTarget::DispatchMode dispatchMode,
+                                    ftl::Flags<InputTarget::Flags> targetFlags,
+                                    std::bitset<MAX_POINTER_ID + 1> pointerIds,
+                                    std::optional<nsecs_t> firstDownTimeInTarget,
+                                    std::optional<ui::LogicalDisplayId> pointerDisplayId,
+                                    std::function<void()> dump,
+                                    std::vector<InputTarget>& inputTargets);
 
         base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
         findTouchedWindowTargets(nsecs_t currentTime, const MotionEntry& entry,
-                                 const ConnectionManager& connections,
-                                 const DispatcherWindowInfo& windowInfos,
                                  const sp<android::gui::WindowInfoHandle> dragWindow,
                                  std::function<void(const MotionEntry&)> addDragEvent,
                                  std::function<void()> dump);
@@ -396,9 +417,9 @@
         bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId,
                                DeviceId deviceId, int32_t pointerId) const;
 
-        // Find touched windowHandle and display by token.
-        std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
-        findTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const;
+        // Find an existing touched windowHandle and display by token.
+        std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+        findExistingTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const;
 
         void forAllTouchedWindows(std::function<void(const sp<gui::WindowInfoHandle>&)> f) const;
 
@@ -410,8 +431,7 @@
 
         // Updates the touchState for display from WindowInfo,
         // returns list of CancellationArgs for every cancelled touch
-        std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId,
-                                                         const DispatcherWindowInfo& windowInfos);
+        std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId);
 
         void removeAllPointersForDevice(DeviceId deviceId);
 
@@ -420,9 +440,7 @@
         std::optional<
                 std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>,
                            std::list<CancellationArgs>, std::list<PointerDownArgs>>>
-        transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
-                             const DispatcherWindowInfo& windowInfos,
-                             const ConnectionManager& connections);
+        transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
 
         base::Result<std::list<CancellationArgs>, status_t> pilferPointers(
                 const sp<IBinder>& token, const Connection& requestingConnection);
@@ -432,6 +450,29 @@
     private:
         std::unordered_map<ui::LogicalDisplayId, TouchState> mTouchStatesByDisplay;
 
+        // As there can be only one CursorState per topology group, we will treat all displays in
+        // the topology as one connected display-group. These will be identified by
+        // DisplayTopologyGraph::primaryDisplayId.
+        // Cursor on the any of the displays that are not part of the topology will be identified by
+        // the displayId similar to mTouchStatesByDisplay.
+        std::unordered_map<ui::LogicalDisplayId, TouchState> mCursorStateByDisplay;
+
+        // The supplied lambda is invoked for each touch and cursor state of the display.
+        // The function iterates until the lambda returns true, effectively performing a 'break'
+        // from the iteration.
+        void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+                                              std::function<bool(const TouchState&)> f) const;
+
+        void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+                                              std::function<bool(TouchState&)> f);
+
+        // The supplied lambda is invoked for each touchState. The function iterates until
+        // the lambda returns true, effectively performing a 'break' from the iteration.
+        void forAllTouchAndCursorStates(
+                std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const;
+
+        void forAllTouchAndCursorStates(std::function<bool(ui::LogicalDisplayId, TouchState&)> f);
+
         std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
         findTouchStateWindowAndDisplay(const sp<IBinder>& token);
 
@@ -440,30 +481,31 @@
                 const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
                 DeviceId deviceId, const std::vector<PointerProperties>& pointers,
                 ftl::Flags<InputTarget::Flags> oldTargetFlags,
-                ftl::Flags<InputTarget::Flags> newTargetFlags,
-                const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections);
+                ftl::Flags<InputTarget::Flags> newTargetFlags);
 
-        bool canWindowReceiveMotion(const sp<android::gui::WindowInfoHandle>& window,
-                                    const MotionEntry& motionEntry,
-                                    const ConnectionManager& connections,
-                                    const DispatcherWindowInfo& windowInfos) const;
+        void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState);
+
+        void eraseTouchStateForMotionEntry(const MotionEntry& entry);
+
+        const TouchState* getTouchStateForMotionEntry(
+                const android::inputdispatcher::MotionEntry& entry) const;
+
+        bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window,
+                                    const MotionEntry& motionEntry) const;
 
         // Return true if stylus is currently down anywhere on the specified display,
         // and false otherwise.
         bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId) const;
 
-        static std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
-                TouchState& state, ui::LogicalDisplayId displayId,
-                const DispatcherWindowInfo& windowInfos);
+        std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
+                TouchState& state, ui::LogicalDisplayId displayId);
 
-        static std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
-                TouchState& state, ui::LogicalDisplayId displayId,
-                const DispatcherWindowInfo& windowInfos);
+        std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
+                TouchState& state, ui::LogicalDisplayId displayId);
 
-        static std::vector<InputTarget> findOutsideTargets(
-                ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
-                int32_t pointerId, const ConnectionManager& connections,
-                const DispatcherWindowInfo& windowInfos, std::function<void()> dump);
+        std::vector<InputTarget> findOutsideTargets(ui::LogicalDisplayId displayId,
+                                                    const sp<gui::WindowInfoHandle>& touchedWindow,
+                                                    int32_t pointerId, std::function<void()> dump);
 
         /**
          * Slip the wallpaper touch if necessary.
@@ -478,18 +520,18 @@
          * @param targets the current targets to add the walpaper ones to
          * @param eventTime the new downTime for the wallpaper target
          */
-        static void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
-                                       const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
-                                       const sp<android::gui::WindowInfoHandle>& newWindowHandle,
-                                       TouchState& state, const MotionEntry& entry,
-                                       std::vector<InputTarget>& targets,
-                                       const ConnectionManager& connections,
-                                       const DispatcherWindowInfo& windowInfos,
-                                       std::function<void()> dump);
+        void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
+                                const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+                                const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+                                TouchState& state, const MotionEntry& entry,
+                                std::vector<InputTarget>& targets, std::function<void()> dump);
 
-        static ftl::Flags<InputTarget::Flags> getTargetFlags(
+        ftl::Flags<InputTarget::Flags> getTargetFlags(
                 const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition,
-                bool isSplit, const DispatcherWindowInfo& windowInfos);
+                bool isSplit);
+
+        const DispatcherWindowInfo& mWindowInfos;
+        const ConnectionManager& mConnectionManager;
     };
 
     DispatcherTouchState mTouchStates GUARDED_BY(mLock);
@@ -876,10 +918,14 @@
     std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
             const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
             bool handled) REQUIRES(mLock);
+    void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
+                                      std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
 
     // Statistics gathering.
     nsecs_t mLastStatisticPushTime = 0;
     std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock);
+    // Must outlive `mLatencyTracker`.
+    std::vector<InputDeviceInfo> mInputDevices;
     LatencyTracker mLatencyTracker GUARDED_BY(mLock);
     void traceInboundQueueLengthLocked() REQUIRES(mLock);
     void traceOutboundQueueLength(const Connection& connection);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 9b5a79b..782a54f 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -15,7 +15,9 @@
  */
 
 #include "DebugConfig.h"
+#include "input/Input.h"
 #include "input/InputDevice.h"
+#include "input/InputFlags.h"
 
 #include "InputState.h"
 
@@ -221,10 +223,15 @@
 }
 
 ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
+    // If we have connected displays a mouse can move between displays and displayId may change
+    // while a gesture is in-progress.
+    const bool skipDisplayCheck =
+            InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source);
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos[i];
         if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
-            memento.displayId == entry.displayId && memento.hovering == hovering) {
+            memento.hovering == hovering &&
+            (skipDisplayCheck || memento.displayId == entry.displayId)) {
             return i;
         }
     }
@@ -338,7 +345,10 @@
         // would receive different events from each display. Since the TouchStates are per-display,
         // it's unlikely that those two streams would be consistent with each other. Therefore,
         // cancel the previous gesture if the display id changes.
-        if (motionEntry.displayId != lastMemento.displayId) {
+        // Except when we have connected-displays where a mouse may move across display boundaries.
+        const bool skipDisplayCheck = (InputFlags::connectedDisplaysCursorEnabled() &&
+                                       isMouseOrTouchpad(motionEntry.source));
+        if (!skipDisplayCheck && motionEntry.displayId != lastMemento.displayId) {
             LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
                       << " and new event is " << motionEntry;
             return true;
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 0921e37..7c23694 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -67,8 +67,9 @@
 
 } // namespace
 
-LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor)
-      : mTimelineProcessor(&processor) {}
+LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor,
+                               std::vector<InputDeviceInfo>& inputDevices)
+      : mTimelineProcessor(&processor), mInputDevices(inputDevices) {}
 
 void LatencyTracker::trackListener(const NotifyArgs& args) {
     if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) {
@@ -248,8 +249,4 @@
             StringPrintf("%s  mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
 }
 
-void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) {
-    mInputDevices = inputDevices;
-}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 79ea14c..7e2cc79 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -20,9 +20,11 @@
 
 #include <map>
 #include <unordered_map>
+#include <vector>
 
 #include <binder/IBinder.h>
 #include <input/Input.h>
+#include <input/InputDevice.h>
 
 #include "InputEventTimeline.h"
 #include "NotifyArgs.h"
@@ -41,8 +43,10 @@
     /**
      * Create a LatencyTracker.
      * param reportingFunction: the function that will be called in order to report full latency.
+     * param inputDevices: input devices relevant for tracking.
      */
-    LatencyTracker(InputEventTimelineProcessor& processor);
+    LatencyTracker(InputEventTimelineProcessor& processor,
+                   std::vector<InputDeviceInfo>& inputDevices);
     /**
      * Start keeping track of an event identified by the args. This must be called first.
      * If duplicate events are encountered (events that have the same eventId), none of them will be
@@ -60,7 +64,6 @@
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
     std::string dump(const char* prefix) const;
-    void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
 
 private:
     /**
@@ -81,7 +84,7 @@
     std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
 
     InputEventTimelineProcessor* mTimelineProcessor;
-    std::vector<InputDeviceInfo> mInputDevices;
+    std::vector<InputDeviceInfo>& mInputDevices;
 
     void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
                        const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
diff --git a/services/inputflinger/docs/device_configuration.md b/services/inputflinger/docs/device_configuration.md
new file mode 100644
index 0000000..0b75eb2
--- /dev/null
+++ b/services/inputflinger/docs/device_configuration.md
@@ -0,0 +1,10 @@
+# Input Device Configuration
+
+There are a number of properties that can be specified for an input device.
+
+|Property|Value|
+|---|----|
+|`audio.mic`|A boolean (`0` or `1`) that indicates whether the device has a microphone.|
+|`device.additionalSysfsLedsNode`|A string representing the path to search for device lights to be used in addition to searching the device node itself for lights.|
+|`device.internal`|A boolean (`0` or `1`) that indicates if this input device is part of the device as opposed to be externally attached.|
+|`device.type`|A string representing if the device is of a certain type. Valid values include `rotaryEncoder` and `externalStylus`.
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 36614b2..c805b74 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -61,6 +61,16 @@
 
     /* Notifies that mouse cursor faded due to typing. */
     virtual void notifyMouseCursorFadedOnTyping() = 0;
+
+    /**
+     * Give accessibility a chance to filter motion event by pointer devices.
+     * The return values denotes the delta x and y after filtering it.
+     *
+     * This call happens on the input hot path and it is extremely performance sensitive.
+     * This also must not call back into native code.
+     */
+    virtual std::optional<vec2> filterPointerMotionForAccessibility(
+            const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3934e78..dc7f7c1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -66,9 +66,10 @@
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
         "mapper/gestures/GestureConverter.cpp",
-        "mapper/gestures/GesturesLogging.cpp",
+        "mapper/gestures/GesturesLogcatAdapter.cpp",
         "mapper/gestures/HardwareProperties.cpp",
         "mapper/gestures/HardwareStateConverter.cpp",
+        "mapper/gestures/Logging.cpp",
         "mapper/gestures/PropertyProvider.cpp",
         "mapper/gestures/TimerProvider.cpp",
     ],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 3c8b6f5..2fcb5d8 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -351,6 +351,22 @@
     return colors;
 }
 
+static base::Result<std::shared_ptr<PropertyMap>> loadConfiguration(
+        const InputDeviceIdentifier& ident) {
+    std::string configurationFile =
+            getInputDeviceConfigurationFilePathByDeviceIdentifier(ident,
+                                                                  InputDeviceConfigurationFileType::
+                                                                          CONFIGURATION);
+    if (configurationFile.empty()) {
+        ALOGD("No input device configuration file found for device '%s'.", ident.name.c_str());
+        return base::Result<std::shared_ptr<PropertyMap>>(nullptr);
+    }
+    base::Result<std::shared_ptr<PropertyMap>> propertyMap =
+            PropertyMap::load(configurationFile.c_str());
+
+    return propertyMap;
+}
+
 /**
  * Read country code information exposed through the sysfs path and convert it to Layout info.
  */
@@ -409,11 +425,22 @@
  *  Read information about lights exposed through the sysfs path.
  */
 static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration(
-        const std::filesystem::path& sysfsRootPath) {
+        const std::filesystem::path& sysfsRootPath, const std::shared_ptr<PropertyMap>& config) {
     std::unordered_map<int32_t, RawLightInfo> lightInfos;
     int32_t nextLightId = 0;
-    // Check if device has any lights.
-    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+    // Check if device has any lights.  If the Input Device Configuration file specifies any lights,
+    // use those in addition to searching the device node itself for lights.
+    std::vector<std::filesystem::path> paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+
+    if (config) {
+        auto additionalLights = config->getString("device.additionalSysfsLedsNode");
+        if (additionalLights) {
+            ALOGI("IDC specifies additional path for lights at '%s'",
+                  additionalLights.value().c_str());
+            paths.push_back(std::filesystem::path(additionalLights.value()));
+        }
+    }
+
     for (const auto& nodePath : paths) {
         RawLightInfo info;
         info.id = ++nextLightId;
@@ -532,17 +559,16 @@
 // --- EventHub::Device ---
 
 EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
-                         std::shared_ptr<const AssociatedDevice> assocDev)
+                         std::shared_ptr<PropertyMap> config)
       : fd(fd),
         id(id),
         path(std::move(path)),
         identifier(std::move(identifier)),
         classes(0),
-        configuration(nullptr),
+        configuration(std::move(config)),
         virtualKeyMap(nullptr),
         ffEffectPlaying(false),
         ffEffectId(-1),
-        associatedDevice(std::move(assocDev)),
         controllerNumber(0),
         enabled(true),
         isVirtual(fd < 0),
@@ -696,26 +722,6 @@
     return false;
 }
 
-void EventHub::Device::loadConfigurationLocked() {
-    configurationFile =
-            getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
-                                                                  InputDeviceConfigurationFileType::
-                                                                          CONFIGURATION);
-    if (configurationFile.empty()) {
-        ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
-    } else {
-        android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
-                PropertyMap::load(configurationFile.c_str());
-        if (!propertyMap.ok()) {
-            ALOGE("Error loading input device configuration file for device '%s'.  "
-                  "Using default configuration.",
-                  identifier.name.c_str());
-        } else {
-            configuration = std::move(*propertyMap);
-        }
-    }
-}
-
 bool EventHub::Device::loadVirtualKeyMapLocked() {
     // The virtual key map is supplied by the kernel as a system board property file.
     std::string propPath = "/sys/board_properties/virtualkeys.";
@@ -1611,7 +1617,7 @@
 }
 
 std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked(
-        const std::filesystem::path& devicePath) const {
+        const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const {
     const std::optional<std::filesystem::path> sysfsRootPathOpt =
             getSysfsRootPath(devicePath.c_str());
     if (!sysfsRootPathOpt) {
@@ -1628,8 +1634,13 @@
         if (!associatedDevice) {
             // Found matching associated device for the first time.
             associatedDevice = dev->associatedDevice;
-            // Reload this associated device if needed.
-            const auto reloadedDevice = AssociatedDevice(path);
+            // Reload this associated device if needed.  Use the base device
+            // config.  Note that this will essentially arbitrarily pick one
+            // Device as the base for the AssociatedDevice configuration.  If
+            // there are multiple Device's that have a configuration for the
+            // AssociatedDevice, only one configuration will be chosen and will
+            // be used for all other AssociatedDevices for the same sysfs path.
+            const auto reloadedDevice = AssociatedDevice(path, associatedDevice->baseDevConfig);
             if (reloadedDevice != *dev->associatedDevice) {
                 ALOGI("The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
                       path.c_str(), associatedDevice->dump().c_str());
@@ -1642,16 +1653,18 @@
 
     if (!associatedDevice) {
         // No existing associated device found for this path, so create a new one.
-        associatedDevice = std::make_shared<AssociatedDevice>(path);
+        associatedDevice = std::make_shared<AssociatedDevice>(path, config);
     }
 
     return associatedDevice;
 }
 
-EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath)
+EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+                                             std::shared_ptr<PropertyMap> config)
       : sysfsRootPath(sysfsRootPath),
+        baseDevConfig(std::move(config)),
         batteryInfos(readBatteryConfiguration(sysfsRootPath)),
-        lightInfos(readLightsConfiguration(sysfsRootPath)),
+        lightInfos(readLightsConfiguration(sysfsRootPath, baseDevConfig)),
         layoutInfo(readLayoutConfiguration(sysfsRootPath)) {}
 
 std::string EventHub::AssociatedDevice::dump() const {
@@ -2337,11 +2350,21 @@
     // Fill in the descriptor.
     assignDescriptorLocked(identifier);
 
+    // Load the configuration file for the device.
+    std::shared_ptr<PropertyMap> configuration = nullptr;
+    base::Result<std::shared_ptr<PropertyMap>> propertyMapResult = loadConfiguration(identifier);
+    if (!propertyMapResult.ok()) {
+        ALOGE("Error loading input device configuration file for device '%s'. "
+              "Using default configuration. Error: %s",
+              identifier.name.c_str(), propertyMapResult.error().message().c_str());
+    } else {
+        configuration = propertyMapResult.value();
+    }
+
     // Allocate device.  (The device object takes ownership of the fd at this point.)
     int32_t deviceId = mNextDeviceId++;
     std::unique_ptr<Device> device =
-            std::make_unique<Device>(fd, deviceId, devicePath, identifier,
-                                     obtainAssociatedDeviceLocked(devicePath));
+            std::make_unique<Device>(fd, deviceId, devicePath, identifier, configuration);
 
     ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
     ALOGV("  bus:        %04x\n"
@@ -2356,8 +2379,8 @@
     ALOGV("  driver:     v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff,
           driverVersion & 0xff);
 
-    // Load the configuration file for the device.
-    device->loadConfigurationLocked();
+    // Obtain the associated device, if any.
+    device->associatedDevice = obtainAssociatedDeviceLocked(devicePath, device->configuration);
 
     // Figure out the kinds of events the device reports.
     device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
@@ -2664,7 +2687,8 @@
             testedDevices.emplace(dev.associatedDevice, false);
             return false;
         }
-        auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath);
+        auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath,
+                                               dev.associatedDevice->baseDevConfig);
         const bool changed = *dev.associatedDevice != reloadedDevice;
         testedDevices.emplace(dev.associatedDevice, changed);
         return changed;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 7ab000b..58df692 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -32,6 +32,7 @@
 #include <unistd.h>
 #include <utils/Errors.h>
 #include <utils/Thread.h>
+#include <string>
 
 #include "InputDevice.h"
 #include "include/gestures.h"
@@ -196,9 +197,8 @@
         if (mNextTimeout != LLONG_MAX) {
             nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
             if (now >= mNextTimeout) {
-                if (debugRawEvents()) {
-                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-                }
+                ALOGD_IF(debugRawEvents(), "Timeout expired, latency=%0.3fms",
+                         (now - mNextTimeout) * 0.000001f);
                 mNextTimeout = LLONG_MAX;
                 mPendingArgs += timeoutExpiredLocked(now);
             }
@@ -263,9 +263,7 @@
                 }
                 batchSize += 1;
             }
-            if (debugRawEvents()) {
-                ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-            }
+            ALOGD_IF(debugRawEvents(), "BatchSize: %zu Count: %zu", batchSize, count);
             out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
         } else {
             switch (rawEvent->type) {
@@ -948,7 +946,10 @@
 
 void InputReader::dump(std::string& dump) {
     std::scoped_lock _l(mLock);
+    dumpLocked(dump);
+}
 
+void InputReader::dumpLocked(std::string& dump) {
     mEventHub->dump(dump);
     dump += "\n";
 
@@ -1035,6 +1036,12 @@
 InputReader::ContextImpl::ContextImpl(InputReader* reader)
       : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
 
+std::string InputReader::ContextImpl::dump() {
+    std::string dump;
+    mReader->dumpLocked(dump);
+    return dump;
+}
+
 void InputReader::ContextImpl::updateGlobalMetaState() {
     // lock is already held by the input loop
     mReader->updateGlobalMetaStateLocked();
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 7434ae4..df22890 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -78,10 +78,8 @@
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = brightness * ratio;
     }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
+    ALOGD_IF(DEBUG_LIGHT_DETAILS, "getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+             rawLightId, brightness, ratio);
     return brightness;
 }
 
@@ -97,10 +95,8 @@
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = ceil(brightness / ratio);
     }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
+    ALOGD_IF(DEBUG_LIGHT_DETAILS, "setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+             rawLightId, brightness, ratio);
     context.setLightBrightness(rawLightId, brightness);
 }
 
@@ -453,10 +449,9 @@
         if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
             rawGlobalId = rawId;
         }
-        if (DEBUG_LIGHT_DETAILS) {
-            ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
-                  rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
-        }
+        ALOGD_IF(DEBUG_LIGHT_DETAILS, "Light rawId %d name %s max %d flags %s\n", rawInfo->id,
+                 rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
+                 rawInfo->flags.string().c_str());
     }
 
     // Construct a player ID light
@@ -473,10 +468,8 @@
     }
     // Construct a RGB light for composed RGB light
     if (hasRedLed && hasGreenLed && hasBlueLed) {
-        if (DEBUG_LIGHT_DETAILS) {
-            ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
-                  rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
-        }
+        ALOGD_IF(DEBUG_LIGHT_DETAILS, "Rgb light ids [%d, %d, %d]\n", rawRgbIds.at(LightColor::RED),
+                 rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
         bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
                         keyboardBacklightIds.end() &&
                 keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
@@ -518,9 +511,8 @@
         // If the node is multi-color led, construct a MULTI_COLOR light
         if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
             rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-            }
+            ALOGD_IF(DEBUG_LIGHT_DETAILS, "Multicolor light Id %d name %s\n", rawInfo.id,
+                     rawInfo.name.c_str());
             std::unique_ptr<Light> light =
                     std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
                                                       type, rawInfo.id);
@@ -528,9 +520,8 @@
             continue;
         }
         // Construct a Mono LED light
-        if (DEBUG_LIGHT_DETAILS) {
-            ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-        }
+        ALOGD_IF(DEBUG_LIGHT_DETAILS, "Mono light Id %d name %s\n", rawInfo.id,
+                 rawInfo.name.c_str());
         std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
                                                                    ++mNextId, type, rawInfo.id);
 
@@ -552,10 +543,8 @@
         return false;
     }
     auto& light = it->second;
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
-              ftl::enum_string(light->type).c_str(), color);
-    }
+    ALOGD_IF(DEBUG_LIGHT_DETAILS, "setLightColor lightId %d type %s color 0x%x", lightId,
+             ftl::enum_string(light->type).c_str(), color);
     return light->setLightColor(color);
 }
 
@@ -566,10 +555,8 @@
     }
     auto& light = it->second;
     std::optional<int32_t> color = light->getLightColor();
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
-              ftl::enum_string(light->type).c_str(), color.value_or(0));
-    }
+    ALOGD_IF(DEBUG_LIGHT_DETAILS, "getLightColor lightId %d type %s color 0x%x", lightId,
+             ftl::enum_string(light->type).c_str(), color.value_or(0));
     return color;
 }
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 31ac63f..adbfdeb 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -21,6 +21,7 @@
 #include <filesystem>
 #include <functional>
 #include <map>
+#include <memory>
 #include <optional>
 #include <ostream>
 #include <string>
@@ -87,6 +88,7 @@
  * If any new classes are added, we need to add them in rust input side too.
  */
 enum class InputDeviceClass : uint32_t {
+    // LINT.IfChange
     /* The input device is a keyboard or has buttons. */
     KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD,
 
@@ -143,6 +145,7 @@
 
     /* The input device is external (not built-in). */
     EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL,
+    // LINT.ThenChange(frameworks/native/services/inputflinger/tests/fuzzers/MapperHelpers.h)
 };
 
 enum class SysfsClass : uint32_t {
@@ -619,9 +622,12 @@
 private:
     // Holds information about the sysfs device associated with the Device.
     struct AssociatedDevice {
-        AssociatedDevice(const std::filesystem::path& sysfsRootPath);
+        AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+                         std::shared_ptr<PropertyMap> baseDevConfig);
         // The sysfs root path of the misc device.
         std::filesystem::path sysfsRootPath;
+        // The configuration of the base device.
+        std::shared_ptr<PropertyMap> baseDevConfig;
         std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
         std::optional<RawLayoutInfo> layoutInfo;
@@ -658,7 +664,7 @@
         std::map<int /*axis*/, AxisState> absState;
 
         std::string configurationFile;
-        std::unique_ptr<PropertyMap> configuration;
+        std::shared_ptr<PropertyMap> configuration;
         std::unique_ptr<VirtualKeyMap> virtualKeyMap;
         KeyMap keyMap;
 
@@ -672,7 +678,7 @@
         int32_t controllerNumber;
 
         Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
-               std::shared_ptr<const AssociatedDevice> assocDev);
+               std::shared_ptr<PropertyMap> config);
         ~Device();
 
         void close();
@@ -692,7 +698,6 @@
         void populateAbsoluteAxisStates();
         bool hasKeycodeLocked(int keycode) const;
         bool hasKeycodeInternalLocked(int keycode) const;
-        void loadConfigurationLocked();
         bool loadVirtualKeyMapLocked();
         status_t loadKeyMapLocked();
         bool isExternalDeviceLocked();
@@ -724,7 +729,8 @@
     void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
     std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked(
-            const std::filesystem::path& devicePath) const REQUIRES(mLock);
+            const std::filesystem::path& devicePath,
+            const std::shared_ptr<PropertyMap>& config) const REQUIRES(mLock);
 
     void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
     void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 0d6e102..6a25937 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,6 +21,7 @@
 #include <utils/Mutex.h>
 
 #include <memory>
+#include <string>
 #include <unordered_map>
 #include <vector>
 
@@ -142,6 +143,7 @@
 
     public:
         explicit ContextImpl(InputReader* reader);
+        std::string dump() REQUIRES(mReader->mLock) override;
         // lock is already held by the input loop
         void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
         int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
@@ -216,6 +218,8 @@
     // The input device that produced a new gesture most recently.
     DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID};
 
+    void dumpLocked(std::string& dump) REQUIRES(mLock);
+
     // low-level input event decoding and device management
     [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
             REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 20ed74f..f38fd7b 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -20,6 +20,7 @@
 #include <input/KeyboardClassifier.h>
 #include "NotifyArgs.h"
 
+#include <string>
 #include <vector>
 
 namespace android {
@@ -39,6 +40,8 @@
     InputReaderContext() {}
     virtual ~InputReaderContext() {}
 
+    virtual std::string dump() = 0;
+
     virtual void updateGlobalMetaState() = 0;
     virtual int32_t getGlobalMetaState() = 0;
 
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index dd46bbc..d796af1 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -25,8 +25,6 @@
 #include <linux/input-event-codes.h>
 #include <log/log_main.h>
 
-namespace input_flags = com::android::input::flags;
-
 namespace android {
 
 namespace {
@@ -119,15 +117,10 @@
 }
 
 void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
-    if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
-        tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
-                                         AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
-        tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
-                                         AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
-    } else {
-        tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
-        tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
-    }
+    tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
+                                     AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
+    tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
+                                     AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
     tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
     tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
     tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
@@ -213,13 +206,11 @@
         }
         out.push_back(
                 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
-        if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
-            // For any further events we send from this sync, the pointers won't have moved relative
-            // to the positions we just reported in this MOVE event, so zero out the relative axes.
-            for (PointerCoords& pointer : coords) {
-                pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
-                pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
-            }
+        // For any further events we send from this sync, the pointers won't have moved relative to
+        // the positions we just reported in this MOVE event, so zero out the relative axes.
+        for (PointerCoords& pointer : coords) {
+            pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+            pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
         }
     }
 
@@ -275,9 +266,7 @@
                                      /*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
 
         freePointerIdForSlot(slotNumber);
-        if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
-            mPreviousCoordsForSlotNumber.erase(slotNumber);
-        }
+        mPreviousCoordsForSlotNumber.erase(slotNumber);
         coords.erase(coords.begin() + indexToRemove);
         properties.erase(properties.begin() + indexToRemove);
         // Now that we've removed some coords and properties, we might have to update the slot
@@ -336,15 +325,13 @@
     coords.clear();
     coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
-    if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
-        if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
-            it != mPreviousCoordsForSlotNumber.end()) {
-            auto [oldX, oldY] = it->second;
-            coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
-            coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
-        }
-        mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
+    if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
+        it != mPreviousCoordsForSlotNumber.end()) {
+        auto [oldX, oldY] = it->second;
+        coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
+        coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
     }
+    mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
 
     coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
     coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fd8224a..4d08f19 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -79,19 +79,17 @@
             if (id) {
                 outState->rawPointerData.canceledIdBits.markBit(id.value());
             }
-            if (DEBUG_POINTERS) {
-                ALOGI("Stop processing slot %zu for it received a palm event from device %s",
-                      inIndex, getDeviceName().c_str());
-            }
+            ALOGI_IF(DEBUG_POINTERS,
+                     "Stop processing slot %zu for it received a palm event from device %s",
+                     inIndex, getDeviceName().c_str());
             continue;
         }
 
         if (outCount >= MAX_POINTERS) {
-            if (DEBUG_POINTERS) {
-                ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
-                      "ignoring the rest.",
-                      getDeviceName().c_str(), MAX_POINTERS);
-            }
+            ALOGD_IF(DEBUG_POINTERS,
+                     "MultiTouch device %s emitted more than maximum of %zu pointers; ignoring the "
+                     "rest.",
+                     getDeviceName().c_str(), MAX_POINTERS);
             break; // too many fingers!
         }
 
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 1f6600d..0d1d884 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -235,9 +235,8 @@
     // else calculate difference between previous and current MSC_TIMESTAMP
     if (mPrevMscTime == 0) {
         mHardwareTimestamp = evTime;
-        if (DEBUG_SENSOR_EVENT_DETAILS) {
-            ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp);
-        }
+        ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Initialize hardware timestamp = %" PRId64,
+                 mHardwareTimestamp);
     } else {
         // Calculate the difference between current msc_timestamp and
         // previous msc_timestamp, including when msc_timestamp wraps around.
@@ -330,11 +329,10 @@
 bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType,
                                      std::chrono::microseconds samplingPeriod,
                                      std::chrono::microseconds maxBatchReportLatency) {
-    if (DEBUG_SENSOR_EVENT_DETAILS) {
-        ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
-              ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
-              maxBatchReportLatency.count());
-    }
+    ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS,
+             "Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
+             ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
+             maxBatchReportLatency.count());
 
     if (!setSensorEnabled(sensorType, /*enabled=*/true)) {
         return false;
@@ -355,9 +353,7 @@
 }
 
 void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
-    if (DEBUG_SENSOR_EVENT_DETAILS) {
-        ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
-    }
+    ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Disable Sensor %s", ftl::enum_string(sensorType).c_str());
 
     if (!setSensorEnabled(sensorType, /*enabled=*/false)) {
         return;
@@ -389,15 +385,12 @@
         }
 
         nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
-        if (DEBUG_SENSOR_EVENT_DETAILS) {
-            ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
-                  ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
-        }
+        ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
+                 ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
         if (sensor.lastSampleTimeNs.has_value() &&
             timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
-            if (DEBUG_SENSOR_EVENT_DETAILS) {
-                ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
-            }
+            ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s Skip a sample.",
+                     ftl::enum_string(sensorType).c_str());
         } else {
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
index 9ec02a6..d55df51 100644
--- a/services/inputflinger/reader/mapper/SlopController.cpp
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -54,13 +54,13 @@
     mCumulativeValue += value;
 
     if (abs(mCumulativeValue) >= mSlopThreshold) {
-        ALOGD("SlopController: did not drop event with value .%3f", value);
+        ALOGD("SlopController: did not drop event with value %.3f", value);
         mHasSlopBeenMet = true;
         // Return the amount of value that exceeds the slop.
         return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
     }
 
-    ALOGD("SlopController: dropping event with value .%3f", value);
+    ALOGD("SlopController: dropping event with value %.3f", value);
     return 0;
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8deff6b..4d36db8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -24,6 +24,8 @@
 #include <cinttypes>
 #include <cmath>
 #include <cstddef>
+#include <sstream>
+#include <string>
 #include <tuple>
 
 #include <math.h>
@@ -138,6 +140,40 @@
     *outY = y;
 }
 
+std::ostream& operator<<(std::ostream& out, const RawPointerData::Pointer& p) {
+    out << "id=" << p.id << ", x=" << p.x << ", y=" << p.y << ", pressure=" << p.pressure
+        << ", touchMajor=" << p.touchMajor << ", touchMinor=" << p.touchMinor
+        << ", toolMajor=" << p.toolMajor << ", toolMinor=" << p.toolMinor
+        << ", orientation=" << p.orientation << ", tiltX=" << p.tiltX << ", tiltY=" << p.tiltY
+        << ", distance=" << p.distance << ", toolType=" << ftl::enum_string(p.toolType)
+        << ", isHovering=" << p.isHovering;
+    return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const RawPointerData& data) {
+    out << data.pointerCount << " pointers:\n";
+    for (uint32_t i = 0; i < data.pointerCount; i++) {
+        out << INDENT << "[" << i << "]: " << data.pointers[i] << std::endl;
+    }
+    out << "ID bits: hovering = 0x" << std::hex << std::setfill('0') << std::setw(8)
+        << data.hoveringIdBits.value << ", touching = 0x" << std::setfill('0') << std::setw(8)
+        << data.touchingIdBits.value << ", canceled = 0x" << std::setfill('0') << std::setw(8)
+        << data.canceledIdBits.value << std::dec;
+    return out;
+}
+
+// --- TouchInputMapper::RawState ---
+
+std::ostream& operator<<(std::ostream& out, const TouchInputMapper::RawState& state) {
+    out << "When: " << state.when << std::endl;
+    out << "Read time: " << state.readTime << std::endl;
+    out << "Button state: 0x" << std::setfill('0') << std::setw(8) << std::hex << state.buttonState
+        << std::dec << std::endl;
+    out << "Raw pointer data:" << std::endl;
+    out << addLinePrefix(streamableToString(state.rawPointerData), INDENT);
+    return out;
+}
+
 // --- TouchInputMapper ---
 
 TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext,
@@ -232,20 +268,8 @@
     dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
 
     dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
-    dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
-                         mLastRawState.rawPointerData.pointerCount);
-    for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
-        const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
-        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
-                                     "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
-                                     "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
-                                     "toolType=%s, isHovering=%s\n",
-                             i, pointer.id, pointer.x, pointer.y, pointer.pressure,
-                             pointer.touchMajor, pointer.touchMinor, pointer.toolMajor,
-                             pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY,
-                             pointer.distance, ftl::enum_string(pointer.toolType).c_str(),
-                             toString(pointer.isHovering));
-    }
+    dump += INDENT3 "Last Raw Touch:\n";
+    dump += addLinePrefix(streamableToString(mLastRawState), INDENT4) + "\n";
 
     dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n",
                          mLastCookedState.buttonState);
@@ -1476,6 +1500,22 @@
              last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
              last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
              next.rawPointerData.canceledIdBits.value);
+    if (debugRawEvents() && last.rawPointerData.pointerCount == 0 &&
+        next.rawPointerData.pointerCount == 1) {
+        // Dump a bunch of info to try to debug b/396796958.
+        // TODO(b/396796958): remove this debug dump.
+        ALOGD("pointerCount went from 0 to 1. last:\n%s",
+              addLinePrefix(streamableToString(last), INDENT).c_str());
+        ALOGD("next:\n%s", addLinePrefix(streamableToString(next), INDENT).c_str());
+        ALOGD("InputReader dump:");
+        // The dump is too long to simply add as a format parameter in one log message, so we have
+        // to split it by line and log them individually.
+        std::istringstream stream(mDeviceContext.getContext()->dump());
+        std::string line;
+        while (std::getline(stream, line, '\n')) {
+            ALOGD(INDENT "%s", line.c_str());
+        }
+    }
 
     if (!next.rawPointerData.touchingIdBits.isEmpty() &&
         !next.rawPointerData.hoveringIdBits.isEmpty() &&
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 4ef0be8..45fc6bf 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -348,6 +348,8 @@
         inline void clear() { *this = RawState(); }
     };
 
+    friend std::ostream& operator<<(std::ostream& out, const RawState& state);
+
     struct CookedState {
         // Cooked pointer sample data.
         CookedPointerData cookedPointerData{};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 18a7102..63eb357 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -40,6 +40,7 @@
 #include "TouchCursorInputMapperCommon.h"
 #include "TouchpadInputMapper.h"
 #include "gestures/HardwareProperties.h"
+#include "gestures/Logging.h"
 #include "gestures/TimerProvider.h"
 #include "ui/Rotation.h"
 
@@ -49,15 +50,6 @@
 
 namespace {
 
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
-        __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
-                                  ANDROID_LOG_INFO);
-
 std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
                                                           bool accelerationEnabled,
                                                           size_t propertySize) {
@@ -152,28 +144,39 @@
     // records it if so.
     void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
         std::scoped_lock lock(mLock);
+        Counters& counters = mCounters[id];
         switch (gesture.type) {
             case kGestureTypeFling:
                 if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
                     // Indicates the end of a two-finger scroll gesture.
-                    mCounters[id].twoFingerSwipeGestures++;
+                    counters.twoFingerSwipeGestures++;
                 }
                 break;
             case kGestureTypeSwipeLift:
-                mCounters[id].threeFingerSwipeGestures++;
+                // The Gestures library occasionally outputs two lift gestures in a row, which can
+                // cause inaccurate metrics reporting. To work around this, deduplicate successive
+                // lift gestures.
+                // TODO(b/404529050): fix the Gestures library, and remove this check.
+                if (counters.lastGestureType != kGestureTypeSwipeLift) {
+                    counters.threeFingerSwipeGestures++;
+                }
                 break;
             case kGestureTypeFourFingerSwipeLift:
-                mCounters[id].fourFingerSwipeGestures++;
+                // TODO(b/404529050): fix the Gestures library, and remove this check.
+                if (counters.lastGestureType != kGestureTypeFourFingerSwipeLift) {
+                    counters.fourFingerSwipeGestures++;
+                }
                 break;
             case kGestureTypePinch:
                 if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
-                    mCounters[id].pinchGestures++;
+                    counters.pinchGestures++;
                 }
                 break;
             default:
                 // We're not interested in any other gestures.
                 break;
         }
+        counters.lastGestureType = gesture.type;
     }
 
 private:
@@ -222,6 +225,10 @@
         int32_t threeFingerSwipeGestures = 0;
         int32_t fourFingerSwipeGestures = 0;
         int32_t pinchGestures = 0;
+
+        // Records the last type of gesture received for this device, for deduplication purposes.
+        // TODO(b/404529050): fix the Gestures library and remove this field.
+        GestureType lastGestureType = kGestureTypeContactInitiated;
     };
 
     // Metrics are aggregated by device model and version, so if two devices of the same model and
@@ -470,7 +477,7 @@
 
 std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                              SelfContainedHardwareState schs) {
-    ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
+    ALOGD_IF(debugTouchpadGestures(), "New hardware state: %s", schs.state.String().c_str());
     mGestureInterpreter->PushHardwareState(&schs.state);
     return processGestures(when, readTime);
 }
@@ -481,7 +488,7 @@
 }
 
 void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
-    ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
+    ALOGD_IF(debugTouchpadGestures(), "Gesture ready: %s", gesture->String().c_str());
     if (mResettingInterpreter) {
         // We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should
         // ignore any gestures produced from the interpreter while we're resetting it.
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index a3a48ef..264ef6f 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -43,10 +43,8 @@
 
 std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence,
                                                    ssize_t repeat, int32_t token) {
-    if (DEBUG_VIBRATOR) {
-        ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
-              sequence.toString().c_str(), repeat, token);
-    }
+    ALOGD_IF(DEBUG_VIBRATOR, "vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
+             getDeviceId(), sequence.toString().c_str(), repeat, token);
     std::list<NotifyArgs> out;
 
     mVibrating = true;
@@ -63,9 +61,7 @@
 }
 
 std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
-    if (DEBUG_VIBRATOR) {
-        ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-    }
+    ALOGD_IF(DEBUG_VIBRATOR, "cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
     std::list<NotifyArgs> out;
 
     if (mVibrating && mToken == token) {
@@ -95,9 +91,7 @@
 }
 
 std::list<NotifyArgs> VibratorInputMapper::nextStep() {
-    if (DEBUG_VIBRATOR) {
-        ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
-    }
+    ALOGD_IF(DEBUG_VIBRATOR, "nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
     std::list<NotifyArgs> out;
     mIndex += 1;
     if (size_t(mIndex) >= mSequence.pattern.size()) {
@@ -111,16 +105,11 @@
 
     const VibrationElement& element = mSequence.pattern[mIndex];
     if (element.isOn()) {
-        if (DEBUG_VIBRATOR) {
-            std::string description = element.toString();
-            ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
-                  description.c_str());
-        }
+        ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+                 element.toString().c_str());
         getDeviceContext().vibrate(element);
     } else {
-        if (DEBUG_VIBRATOR) {
-            ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-        }
+        ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
         getDeviceContext().cancelVibrate();
     }
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -128,17 +117,13 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
     mNextStepTime = now + duration.count();
     getContext()->requestTimeoutAtTime(mNextStepTime);
-    if (DEBUG_VIBRATOR) {
-        ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
-    }
+    ALOGD_IF(DEBUG_VIBRATOR, "nextStep: scheduled timeout in %lldms", element.duration.count());
     return out;
 }
 
 NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
     mVibrating = false;
-    if (DEBUG_VIBRATOR) {
-        ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-    }
+    ALOGD_IF(DEBUG_VIBRATOR, "stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 480e276..3255877 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -39,9 +39,6 @@
 
 namespace {
 
-// This will disable the tap to click while the user is typing on a physical keyboard
-const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
-
 // In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
 // re-enabling the tap to click.
 const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
@@ -226,8 +223,7 @@
     if (!mIsHoverCancelled) {
         // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
         // for this case as subsequent handleButtonsChange may choose to ignore this tap.
-        if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
-            (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
+        if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
             enableTapToClick(when);
         }
     }
@@ -278,7 +274,7 @@
             // return early to prevent this tap
             return out;
         }
-    } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
+    } else if (mReaderContext.isPreventingTouchpadTaps()) {
         enableTapToClick(when);
         if (gesture.details.buttons.is_tap) {
             // return early to prevent this tap
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
similarity index 72%
rename from services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
rename to services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
index 26028c5..51905f9 100644
--- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
@@ -22,29 +22,17 @@
 
 #include <log/log.h>
 
+#include "Logging.h"
 #include "include/gestures.h"
 
 extern "C" {
 
-namespace {
-
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
-        __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
-                                  ANDROID_LOG_INFO);
-
-} // namespace
-
 void gestures_log(int verb, const char* fmt, ...) {
     va_list args;
     va_start(args, fmt);
     if (verb == GESTURES_LOG_ERROR) {
         LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
-    } else if (DEBUG_TOUCHPAD_GESTURES) {
+    } else if (android::debugTouchpadGestures()) {
         if (verb == GESTURES_LOG_INFO) {
             LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
         } else {
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 6885adb..3e62f36 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -25,12 +25,8 @@
 #include <com_android_input_flags.h>
 #include <linux/input-event-codes.h>
 
-namespace input_flags = com::android::input::flags;
-
 namespace android {
 
-const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library();
-
 HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
                                                MultiTouchMotionAccumulator& motionAccumulator)
       : mDeviceContext(deviceContext),
@@ -81,18 +77,11 @@
     }
 
     schs.fingers.clear();
-    size_t numPalms = 0;
     for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
         MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
         if (!slot.isInUse()) {
             continue;
         }
-        // Some touchpads continue to report contacts even after they've identified them as palms.
-        // We want to exclude these contacts from the HardwareStates.
-        if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) {
-            numPalms++;
-            continue;
-        }
 
         FingerState& fingerState = schs.fingers.emplace_back();
         fingerState = {};
@@ -105,15 +94,13 @@
         fingerState.position_x = slot.getX();
         fingerState.position_y = slot.getY();
         fingerState.tracking_id = slot.getTrackingId();
-        if (REPORT_PALMS_TO_GESTURES_LIBRARY) {
-            fingerState.tool_type = slot.getToolType() == ToolType::PALM
-                    ? FingerState::ToolType::kPalm
-                    : FingerState::ToolType::kFinger;
-        }
+        fingerState.tool_type = slot.getToolType() == ToolType::PALM
+                ? FingerState::ToolType::kPalm
+                : FingerState::ToolType::kFinger;
     }
     schs.state.fingers = schs.fingers.data();
     schs.state.finger_cnt = schs.fingers.size();
-    schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount() - numPalms;
+    schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
     return schs;
 }
 
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.cpp b/services/inputflinger/reader/mapper/gestures/Logging.cpp
new file mode 100644
index 0000000..b9b97c3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2025 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 "Logging.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+        android::base::GetBoolProperty("ro.debuggable", false);
+#else
+        true;
+#endif
+
+} // namespace
+
+namespace android {
+
+bool debugTouchpadGestures() {
+    if (!IS_DEBUGGABLE_BUILD) {
+        static const bool DEBUG_RAW_EVENTS =
+                __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+                                          ANDROID_LOG_INFO);
+        return DEBUG_RAW_EVENTS;
+    }
+    return __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+                                     ANDROID_LOG_INFO);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.h b/services/inputflinger/reader/mapper/gestures/Logging.h
new file mode 100644
index 0000000..db59fb3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025 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
+
+namespace android {
+
+/**
+ * Log details of touchpad gesture library input, output, and processing.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugTouchpadGestures();
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 18d47f6..677cf1e 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -52,6 +52,7 @@
         "AnrTracker_test.cpp",
         "CapturedTouchpadEventConverter_test.cpp",
         "CursorInputMapper_test.cpp",
+        "DisplayTopologyGraph_test.cpp",
         "EventHub_test.cpp",
         "FakeEventHub.cpp",
         "FakeInputReaderPolicy.cpp",
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 353011a..c6246d9 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -33,8 +33,6 @@
 #include "TestEventMatchers.h"
 #include "TestInputListener.h"
 
-namespace input_flags = com::android::input::flags;
-
 namespace android {
 
 using testing::AllOf;
@@ -50,8 +48,6 @@
             mReader(mFakeEventHub, mFakePolicy, mFakeListener),
             mDevice(newDevice()),
             mDeviceContext(*mDevice, EVENTHUB_ID) {
-        input_flags::include_relative_axis_values_for_captured_touchpads(true);
-
         const size_t slotCount = 8;
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
         mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
diff --git a/services/inputflinger/tests/DisplayTopologyGraph_test.cpp b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp
new file mode 100644
index 0000000..fd2f21c
--- /dev/null
+++ b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2025 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 <gtest/gtest.h>
+#include <input/DisplayTopologyGraph.h>
+
+#include <string>
+#include <string_view>
+#include <tuple>
+
+namespace android {
+
+namespace {
+
+constexpr ui::LogicalDisplayId DISPLAY_ID_1{1};
+constexpr ui::LogicalDisplayId DISPLAY_ID_2{2};
+constexpr int DENSITY_MEDIUM = 160;
+
+} // namespace
+
+using DisplayTopologyGraphTestFixtureParam =
+        std::tuple<std::string_view /*name*/, DisplayTopologyGraph, bool /*isValid*/>;
+
+class DisplayTopologyGraphTestFixture
+      : public testing::Test,
+        public testing::WithParamInterface<DisplayTopologyGraphTestFixtureParam> {};
+
+TEST_P(DisplayTopologyGraphTestFixture, DisplayTopologyGraphTest) {
+    const auto& [_, displayTopology, isValid] = GetParam();
+    EXPECT_EQ(isValid, displayTopology.isValid());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DisplayTopologyGraphTest, DisplayTopologyGraphTestFixture,
+        testing::Values(
+                std::make_tuple(
+                        "InvalidPrimaryDisplay",
+                        DisplayTopologyGraph{.primaryDisplayId = ui::LogicalDisplayId::INVALID,
+                                             .graph = {},
+                                             .displaysDensity = {}},
+                        false),
+                std::make_tuple("PrimaryDisplayNotInGraph",
+                                DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                                     .graph = {},
+                                                     .displaysDensity = {}},
+                                false),
+                std::make_tuple("DisplayDensityMissing",
+                                DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                                     .graph = {{DISPLAY_ID_1, {}}},
+                                                     .displaysDensity = {}},
+                                false),
+                std::make_tuple("ValidSingleDisplayTopology",
+                                DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                                     .graph = {{DISPLAY_ID_1, {}}},
+                                                     .displaysDensity = {{DISPLAY_ID_1,
+                                                                          DENSITY_MEDIUM}}},
+                                true),
+                std::make_tuple(
+                        "MissingReverseEdge",
+                        DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                             .graph = {{DISPLAY_ID_1,
+                                                        {{DISPLAY_ID_2,
+                                                          DisplayTopologyPosition::TOP, 0}}}},
+                                             .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+                                                                 {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+                        false),
+                std::make_tuple(
+                        "IncorrectReverseEdgeDirection",
+                        DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                             .graph = {{DISPLAY_ID_1,
+                                                        {{DISPLAY_ID_2,
+                                                          DisplayTopologyPosition::TOP, 0}}},
+                                                       {DISPLAY_ID_2,
+                                                        {{DISPLAY_ID_1,
+                                                          DisplayTopologyPosition::TOP, 0}}}},
+                                             .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+                                                                 {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+                        false),
+                std::make_tuple(
+                        "IncorrectReverseEdgeOffset",
+                        DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                             .graph = {{DISPLAY_ID_1,
+                                                        {{DISPLAY_ID_2,
+                                                          DisplayTopologyPosition::TOP, 10}}},
+                                                       {DISPLAY_ID_2,
+                                                        {{DISPLAY_ID_1,
+                                                          DisplayTopologyPosition::BOTTOM, 20}}}},
+                                             .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+                                                                 {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+                        false),
+                std::make_tuple(
+                        "ValidMultiDisplayTopology",
+                        DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+                                             .graph = {{DISPLAY_ID_1,
+                                                        {{DISPLAY_ID_2,
+                                                          DisplayTopologyPosition::TOP, 10}}},
+                                                       {DISPLAY_ID_2,
+                                                        {{DISPLAY_ID_1,
+                                                          DisplayTopologyPosition::BOTTOM, -10}}}},
+                                             .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+                                                                 {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+                        true)),
+        [](const testing::TestParamInfo<DisplayTopologyGraphTestFixtureParam>& p) {
+            return std::string{std::get<0>(p.param)};
+        });
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index c4257a8..dcb148f 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -198,10 +198,6 @@
     ASSERT_EQ(token, *receivedToken);
 }
 
-void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
-    mInterceptKeyTimeout = timeout;
-}
-
 std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() {
     return 500ms;
 }
@@ -210,8 +206,9 @@
     mStaleEventTimeout = timeout;
 }
 
-void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
-    mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+void FakeInputDispatcherPolicy::setInterceptKeyBeforeDispatchingResult(
+        std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result) {
+    mInterceptKeyBeforeDispatchingResult = result;
 }
 
 void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) {
@@ -404,7 +401,9 @@
 void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) {
     if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
         // Clear intercept state when we handled the event.
-        mInterceptKeyTimeout = 0ms;
+        if (std::holds_alternative<nsecs_t>(mInterceptKeyBeforeDispatchingResult)) {
+            mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
+        }
     }
 }
 
@@ -414,17 +413,20 @@
 std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
 FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&,
                                                          uint32_t) {
-    if (mConsumeKeyBeforeDispatching) {
-        return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
+    if (std::holds_alternative<inputdispatcher::KeyEntry::InterceptKeyResult>(
+                mInterceptKeyBeforeDispatchingResult)) {
+        return mInterceptKeyBeforeDispatchingResult;
     }
 
-    nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+    nsecs_t delay =
+            std::chrono::nanoseconds(std::get<nsecs_t>(mInterceptKeyBeforeDispatchingResult))
+                    .count();
     if (delay == 0) {
         return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
     }
 
     // Clear intercept state so we could dispatch the event in next wake.
-    mInterceptKeyTimeout = 0ms;
+    mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
     return delay;
 }
 
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index c387eac..b151686 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -96,10 +96,6 @@
     void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
                                 const sp<IBinder>& targetToken);
     void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token);
-    /**
-     * Set policy timeout. A value of zero means next key will not be intercepted.
-     */
-    void setInterceptKeyTimeout(std::chrono::milliseconds timeout);
     std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override;
     void setStaleEventTimeout(std::chrono::nanoseconds timeout);
     void assertUserActivityNotPoked();
@@ -116,7 +112,12 @@
     void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
     void assertUnhandledKeyReported(int32_t keycode);
     void assertUnhandledKeyNotReported();
-    void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
+    /**
+     * Set policy timeout or the interception result.
+     * A timeout value of zero means next key will not be intercepted.
+     */
+    void setInterceptKeyBeforeDispatchingResult(
+            std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result);
     void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay);
 
 private:
@@ -145,11 +146,10 @@
     std::condition_variable mNotifyUserActivity;
     std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
 
-    std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
-
     std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
 
-    bool mConsumeKeyBeforeDispatching = false;
+    std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+            mInterceptKeyBeforeDispatchingResult;
 
     BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 914f5ab..35310a5 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -42,8 +42,6 @@
 
 namespace {
 
-const auto TOUCHPAD_PALM_REJECTION =
-        ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
 const auto TOUCHPAD_PALM_REJECTION_V2 =
         ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection);
 
@@ -1461,7 +1459,6 @@
 }
 
 TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
                   REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
     nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
 
@@ -1574,8 +1571,7 @@
     ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f))));
 }
 
-TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
     // Click should still produce button press/release events
     mReader->getContext()->setPreventingTouchpadTaps(true);
 
@@ -1644,8 +1640,7 @@
     ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
 }
 
-TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
     // initially disable tap-to-click
     mReader->getContext()->setPreventingTouchpadTaps(true);
 
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 34c81fc..ee125bb 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -33,13 +33,6 @@
 
 namespace android {
 
-namespace {
-
-const auto REPORT_PALMS =
-        ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library);
-
-} // namespace
-
 class HardwareStateConverterTest : public testing::Test {
 public:
     HardwareStateConverterTest()
@@ -201,24 +194,7 @@
     EXPECT_EQ(0u, finger2.flags);
 }
 
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms,
-                  REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
-    processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
-    processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
-    std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
-    ASSERT_TRUE(schs.has_value());
-    EXPECT_EQ(0, schs->state.touch_cnt);
-    EXPECT_EQ(0, schs->state.finger_cnt);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms,
-                  REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OnePalm) {
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
@@ -234,54 +210,7 @@
     EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
 }
 
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms,
-                  REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
-    processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
-    processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
-
-    std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
-    ASSERT_TRUE(schs.has_value());
-    EXPECT_EQ(1, schs->state.touch_cnt);
-    EXPECT_EQ(1, schs->state.finger_cnt);
-
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99);
-
-    schs = processSync(ARBITRARY_TIME);
-    ASSERT_TRUE(schs.has_value());
-    EXPECT_EQ(0, schs->state.touch_cnt);
-    ASSERT_EQ(0, schs->state.finger_cnt);
-
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97);
-
-    schs = processSync(ARBITRARY_TIME);
-    ASSERT_TRUE(schs.has_value());
-    EXPECT_EQ(0, schs->state.touch_cnt);
-    EXPECT_EQ(0, schs->state.finger_cnt);
-
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55);
-    processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95);
-    schs = processSync(ARBITRARY_TIME);
-    ASSERT_TRUE(schs.has_value());
-    EXPECT_EQ(1, schs->state.touch_cnt);
-    ASSERT_EQ(1, schs->state.finger_cnt);
-    const FingerState& newFinger = schs->state.fingers[0];
-    EXPECT_EQ(123, newFinger.tracking_id);
-    EXPECT_NEAR(55, newFinger.position_x, EPSILON);
-    EXPECT_NEAR(95, newFinger.position_y, EPSILON);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms,
-                  REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms) {
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
     processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7cc4ff7..f7dcd6c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -37,6 +37,7 @@
 #include <input/BlockingQueue.h>
 #include <input/Input.h>
 #include <input/InputConsumer.h>
+#include <input/KeyCharacterMap.h>
 #include <input/PrintTools.h>
 #include <linux/input.h>
 #include <sys/epoll.h>
@@ -139,6 +140,30 @@
     return event;
 }
 
+InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) {
+    InputDeviceIdentifier identifier;
+    identifier.vendor = vendorId;
+    identifier.product = productId;
+    auto info = InputDeviceInfo();
+    info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
+                    /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID);
+    return info;
+}
+
+std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) {
+    InputDeviceIdentifier identifier;
+    identifier.name = name;
+    std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(),
+                                                                 InputDeviceConfigurationFileType::
+                                                                         KEY_CHARACTER_MAP);
+
+    if (path.empty()) {
+        return nullptr;
+    }
+
+    return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+}
+
 } // namespace
 
 // --- InputDispatcherTest ---
@@ -5491,7 +5516,8 @@
             generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
     const std::chrono::milliseconds interceptKeyTimeout = 50ms;
     const nsecs_t injectTime = keyArgs.eventTime;
-    mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
+    mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+            std::chrono::nanoseconds(interceptKeyTimeout).count());
     mDispatcher->notifyKey(keyArgs);
     // The dispatching time should be always greater than or equal to intercept key timeout.
     window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
@@ -5519,7 +5545,7 @@
 
     // Set a value that's significantly larger than the default consumption timeout. If the
     // implementation is correct, the actual value doesn't matter; it won't slow down the test.
-    mFakePolicy->setInterceptKeyTimeout(600ms);
+    mFakePolicy->setInterceptKeyBeforeDispatchingResult(std::chrono::nanoseconds(600ms).count());
     mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
     // Window should receive key event immediately when same key up.
     window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
@@ -7438,7 +7464,8 @@
 
     window->consumeFocusEvent(true);
 
-    mFakePolicy->setConsumeKeyBeforeDispatching(true);
+    mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+            inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
 
     mDispatcher->notifyKey(
             KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7464,7 +7491,8 @@
 
     window->consumeFocusEvent(true);
 
-    mFakePolicy->setConsumeKeyBeforeDispatching(true);
+    mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+            inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
 
     mDispatcher->notifyKey(
             KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7477,6 +7505,50 @@
     mFakePolicy->assertUserActivityPoked();
 }
 
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) {
+#if !defined(__ANDROID__)
+    GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device.";
+#endif
+    InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0,
+                                                        /*productId=*/0, /*deviceId=*/1);
+    std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic");
+    ASSERT_NE(nullptr, kcm);
+
+    testDevice.setKeyCharacterMap(std::move(kcm));
+    mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice}));
+
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+                                       ui::LogicalDisplayId::DEFAULT);
+
+    window->setFocusable(true);
+    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+    setFocusedWindow(window);
+
+    window->consumeFocusEvent(true);
+
+    mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+            inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK);
+
+    // In the Generic KCM fallbacks, Meta + Space => SEARCH.
+    mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+                                   .keyCode(AKEYCODE_SPACE)
+                                   .metaState(AMETA_META_ON)
+                                   .build());
+    mDispatcher->waitForIdle();
+
+    // Should have poked user activity
+    mFakePolicy->assertUserActivityPoked();
+
+    // Fallback is generated and sent instead.
+    std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false);
+    ASSERT_NE(nullptr, consumedEvent);
+    ASSERT_THAT(*consumedEvent,
+                AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH),
+                      WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+}
+
 class DisableUserActivityInputDispatcherTest : public InputDispatcherTest,
                                                public ::testing::WithParamInterface<bool> {};
 
@@ -9307,6 +9379,24 @@
     mWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterFocusedWindowChanged) {
+    sp<FakeWindowHandle> anotherWindow =
+            sp<FakeWindowHandle>::make(mApp, mDispatcher, "AnotherWindow",
+                                       ui::LogicalDisplayId::DEFAULT);
+    anotherWindow->setFocusable(true);
+    mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *anotherWindow->getInfo()}, {}, 0, 0});
+
+    sendAndConsumeKeyDown(/*deviceId=*/1);
+    expectKeyRepeatOnce(/*repeatCount=*/1);
+    expectKeyRepeatOnce(/*repeatCount=*/2);
+    setFocusedWindow(anotherWindow);
+    anotherWindow->consumeFocusEvent(true);
+
+    // Window should receive key up event with cancel.
+    mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT, AKEY_EVENT_FLAG_CANCELED);
+    anotherWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
     sendAndConsumeKeyDown(/*deviceId=*/1);
     expectKeyRepeatOnce(/*repeatCount=*/1);
@@ -9924,6 +10014,9 @@
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
+        // Use current time as start time otherwise events may be dropped due to being stale.
+        mGestureStartTime = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+
         std::shared_ptr<FakeApplicationHandle> application =
                 std::make_shared<FakeApplicationHandle>();
         application->setDispatchingTimeout(100ms);
@@ -9941,82 +10034,81 @@
         mWindow->consumeFocusEvent(true);
     }
 
-    void notifyAndConsumeMotion(int32_t action, uint32_t source, ui::LogicalDisplayId displayId,
-                                nsecs_t eventTime) {
-        mDispatcher->notifyMotion(MotionArgsBuilder(action, source)
-                                          .displayId(displayId)
-                                          .eventTime(eventTime)
-                                          .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
-                                          .build());
+    NotifyMotionArgs notifyAndConsumeMotion(int32_t action, uint32_t source,
+                                            ui::LogicalDisplayId displayId,
+                                            std::chrono::nanoseconds timeDelay) {
+        const NotifyMotionArgs motionArgs =
+                MotionArgsBuilder(action, source)
+                        .displayId(displayId)
+                        .eventTime((mGestureStartTime + timeDelay).count())
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                        .build();
+        mDispatcher->notifyMotion(motionArgs);
         mWindow->consumeMotionEvent(WithMotionAction(action));
+        return motionArgs;
     }
 
 private:
     sp<FakeWindowHandle> mWindow;
+    std::chrono::nanoseconds mGestureStartTime;
 };
 
 TEST_F_WITH_FLAGS(
         InputDispatcherUserActivityPokeTests, MinPokeTimeObserved,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
-    // Use current time otherwise events may be dropped due to being stale.
-    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
     mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
 
     // First event of type TOUCH. Should poke.
-    notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(50));
+    NotifyMotionArgs motionArgs =
+            notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(50));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
 
     // 80ns > 50ns has passed since previous TOUCH event. Should poke.
-    notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(130));
+    motionArgs =
+            notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(130));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
 
     // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
-    notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(135));
+    motionArgs =
+            notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(135));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
 
     // Within 50ns of previous TOUCH event. Should NOT poke.
     notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(140));
+                           std::chrono::milliseconds(140));
     mFakePolicy->assertUserActivityNotPoked();
 
     // Within 50ns of previous OTHER event. Should NOT poke.
     notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(150));
+                           ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(150));
     mFakePolicy->assertUserActivityNotPoked();
 
     // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
     // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(160));
+                           std::chrono::milliseconds(160));
     mFakePolicy->assertUserActivityNotPoked();
 
     // 65ns > 50ns has passed since previous OTHER event. Should poke.
-    notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(200));
+    motionArgs =
+            notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
 
     // 170ns > 50ns has passed since previous TOUCH event. Should poke.
-    notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(300));
+    motionArgs =
+            notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
+                                   std::chrono::milliseconds(300));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
 
     // Assert that there's no more user activity poke event.
     mFakePolicy->assertUserActivityNotPoked();
@@ -10026,39 +10118,35 @@
         InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
-    // Use current time otherwise events may be dropped due to being stale.
-    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(200));
+    NotifyMotionArgs motionArgs =
+            notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
 
     notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(280));
+                           std::chrono::milliseconds(280));
     mFakePolicy->assertUserActivityNotPoked();
 
-    notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + milliseconds_to_nanoseconds(340));
+    motionArgs =
+            notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                   ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(340));
     mFakePolicy->assertUserActivityPoked(
-            {{currentTime + milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
-              ui::LogicalDisplayId::DEFAULT}});
+            {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
 }
 
 TEST_F_WITH_FLAGS(
         InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
-    // Use current time otherwise events may be dropped due to being stale.
-    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
 
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + 20);
+                           std::chrono::milliseconds(20));
     mFakePolicy->assertUserActivityPoked();
 
     notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           currentTime + 30);
+                           std::chrono::milliseconds(30));
     mFakePolicy->assertUserActivityPoked();
 }
 
@@ -12376,6 +12464,11 @@
     sp<FakeWindowHandle> mSecondWindow;
     sp<FakeWindowHandle> mDragWindow;
     sp<FakeWindowHandle> mSpyWindow;
+
+    std::vector<gui::DisplayInfo> mDisplayInfos;
+
+    std::shared_ptr<FakeApplicationHandle> mSecondApplication;
+    sp<FakeWindowHandle> mWindowOnSecondDisplay;
     // Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
     static constexpr int32_t MOUSE_POINTER_ID = 1;
 
@@ -12396,26 +12489,37 @@
         mSpyWindow->setTrustedOverlay(true);
         mSpyWindow->setFrame(Rect(0, 0, 200, 100));
 
+        mSecondApplication = std::make_shared<FakeApplicationHandle>();
+        mWindowOnSecondDisplay =
+                sp<FakeWindowHandle>::make(mSecondApplication, mDispatcher,
+                                           "TestWindowOnSecondDisplay", SECOND_DISPLAY_ID);
+        mWindowOnSecondDisplay->setFrame({0, 0, 100, 100});
+
         mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
         mDispatcher->onWindowInfosChanged(
-                {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
-                 {},
+                {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo(),
+                  *mWindowOnSecondDisplay->getInfo()},
+                 mDisplayInfos,
                  0,
                  0});
     }
 
-    void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+    void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+                    ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) {
         bool consumeButtonPress = false;
+        const PointF location =
+                displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450);
         switch (fromSource) {
             case AINPUT_SOURCE_TOUCHSCREEN: {
                 ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-                          injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
-                                           ui::LogicalDisplayId::DEFAULT, {50, 50}))
+                          injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+                                           location))
                         << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
                 break;
             }
             case AINPUT_SOURCE_STYLUS: {
-                PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50);
+                PointerBuilder pointer =
+                        PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y);
                 ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                           injectMotionEvent(*mDispatcher,
                                             MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
@@ -12438,12 +12542,14 @@
                 break;
             }
             case AINPUT_SOURCE_MOUSE: {
-                PointerBuilder pointer =
-                        PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50);
+                PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
+                                                 .x(location.x)
+                                                 .y(location.y);
                 ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                           injectMotionEvent(*mDispatcher,
                                             MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
                                                                AINPUT_SOURCE_MOUSE)
+                                                    .displayId(displayId)
                                                     .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
                                                     .pointer(pointer)
                                                     .build()));
@@ -12451,6 +12557,7 @@
                           injectMotionEvent(*mDispatcher,
                                             MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
                                                                AINPUT_SOURCE_MOUSE)
+                                                    .displayId(displayId)
                                                     .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
                                                     .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
                                                     .pointer(pointer)
@@ -12464,43 +12571,59 @@
         }
 
         // Window should receive motion event.
-        mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+        sp<FakeWindowHandle>& targetWindow =
+                displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay;
+        targetWindow->consumeMotionDown(displayId);
         if (consumeButtonPress) {
-            mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+            targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
         }
-        // Spy window should also receive motion event
-        mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+
+        // Spy window should also receive motion event if event is on the same display.
+        if (displayId == ui::LogicalDisplayId::DEFAULT) {
+            mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+        }
     }
 
     // Start performing drag, we will create a drag window and transfer touch to it.
     // @param sendDown : if true, send a motion down on first window before perform drag and drop.
     // Returns true on success.
-    bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+    bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+                   ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) {
         if (sendDown) {
-            injectDown(fromSource);
+            injectDown(fromSource, dragStartDisplay);
         }
 
         // The drag window covers the entire display
-        mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow",
-                                                 ui::LogicalDisplayId::DEFAULT);
+        mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay);
         mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
-        mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
-                                            *mWindow->getInfo(), *mSecondWindow->getInfo()},
-                                           {},
-                                           0,
-                                           0});
+        mDispatcher->onWindowInfosChanged(
+                {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+                  *mSecondWindow->getInfo(), *mWindowOnSecondDisplay->getInfo()},
+                 mDisplayInfos,
+                 0,
+                 0});
+
+        sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT
+                ? mWindow
+                : mWindowOnSecondDisplay;
 
         // Transfer touch focus to the drag window
         bool transferred =
-                mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(),
+                mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(),
                                                   /*isDragDrop=*/true);
         if (transferred) {
-            mWindow->consumeMotionCancel();
-            mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
-                                           AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+            targetWindow->consumeMotionCancel(dragStartDisplay);
+            mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
         }
         return transferred;
     }
+
+    void addDisplay(ui::LogicalDisplayId displayId, ui::Transform transform) {
+        gui::DisplayInfo displayInfo;
+        displayInfo.displayId = displayId;
+        displayInfo.transform = transform;
+        mDisplayInfos.push_back(displayInfo);
+    }
 };
 
 TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
@@ -15187,4 +15310,230 @@
 
 INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool());
 
+class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests {
+    constexpr static int DENSITY_MEDIUM = 160;
+
+    const DisplayTopologyGraph
+            mTopology{.primaryDisplayId = DISPLAY_ID,
+                      .graph = {{DISPLAY_ID,
+                                 {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}},
+                                {SECOND_DISPLAY_ID,
+                                 {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}},
+                      .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM},
+                                          {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}};
+
+protected:
+    void SetUp() override {
+        addDisplay(DISPLAY_ID, ui::Transform());
+        addDisplay(SECOND_DISPLAY_ID,
+                   ui::Transform(ui::Transform::ROT_270, /*logicalDisplayWidth=*/
+                                 500, /*logicalDisplayHeight=*/500));
+
+        InputDispatcherDragTests::SetUp();
+
+        mDispatcher->setDisplayTopology(mTopology);
+    }
+};
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    // pointer-down
+    mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .displayId(DISPLAY_ID)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+                                      .build());
+    mWindow->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+                    .displayId(DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+                    .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                                      WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+    // pointer-move
+    mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .displayId(DISPLAY_ID)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+                                      .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                                      WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+    // pointer-move with different display
+    // TODO (b/383092013): by default windows will not be topology aware and receive events as it
+    // was in the same display. This behaviour has not been implemented yet.
+    mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .displayId(SECOND_DISPLAY_ID)
+                                      .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+                                      .build());
+    // events should be delivered with the second displayId and in corrosponding coordinate space
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                                      WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+    // pointer-up
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, AINPUT_SOURCE_MOUSE)
+                    .displayId(SECOND_DISPLAY_ID)
+                    .buttonState(0)
+                    .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+                    .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                                      WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                                      .displayId(SECOND_DISPLAY_ID)
+                                      .buttonState(0)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+                                      .build());
+    mWindow->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE));
+    // Move on window.
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                    .displayId(DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+    mWindowOnSecondDisplay->assertNoEvents();
+
+    // Move to another window.
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                    .displayId(DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(150).y(50))
+                    .build());
+    mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+    mWindowOnSecondDisplay->assertNoEvents();
+
+    // Move to window on the second display
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                    .displayId(SECOND_DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mWindow->assertNoEvents();
+    mSecondWindow->consumeDragEvent(true, -50, 50);
+    mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+    // drop on the second display
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                    .displayId(SECOND_DISPLAY_ID)
+                    .buttonState(0)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionUp(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindowOnSecondDisplay->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mWindowOnSecondDisplay->assertNoEvents();
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID));
+    // Move on window.
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                    .displayId(SECOND_DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+    // Move to window on the primary display
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                    .displayId(DISPLAY_ID)
+                    .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+    mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50);
+
+    // drop on the primary display
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                    .displayId(DISPLAY_ID)
+                    .buttonState(0)
+                    .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+                    .build());
+    mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+    mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mWindowOnSecondDisplay->assertNoEvents();
+}
+
+using InputDispatcherConnectedDisplayPointerInWindowTest = InputDispatcherConnectedDisplayTest;
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnPrimaryDisplay) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+                    .build());
+
+    mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+    mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+    mWindowOnSecondDisplay->assertNoEvents();
+
+    ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+                                               /*pointerId=*/0));
+    ASSERT_TRUE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+                                               /*pointerId=*/0));
+    ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+                                                SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnNonPrimaryDisplay) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    mDispatcher->notifyMotion(
+            MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+                    .displayId(SECOND_DISPLAY_ID)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+                    .build());
+
+    mWindow->assertNoEvents();
+    mSpyWindow->assertNoEvents();
+    mWindowOnSecondDisplay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+                                                /*pointerId=*/0));
+    ASSERT_FALSE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+                                                /*pointerId=*/0));
+    ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+                                               SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index dce5472..d4e4bb0 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -50,6 +50,8 @@
 
 class MockInputReaderContext : public InputReaderContext {
 public:
+    std::string dump() override { return "(dump from MockInputReaderContext)"; }
+
     MOCK_METHOD(void, updateGlobalMetaState, (), (override));
     MOCK_METHOD(int32_t, getGlobalMetaState, (), (override));
 
@@ -191,6 +193,9 @@
                 (ui::LogicalDisplayId displayId, const vec2& position), (override));
     MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
     MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
+    MOCK_METHOD(std::optional<vec2>, filterPointerMotionForAccessibility,
+                (const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId),
+                (override));
 };
 
 class MockInputDevice : public InputDevice {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 17acdd4..d4b15bc 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -176,16 +176,22 @@
         return std::get<NotifyKeyArgs>(args.front());
     }
 
-    void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode,
-                             int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) {
-        std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1);
+    std::list<NotifyArgs> processKeyAndSync(nsecs_t when, int32_t code, int32_t value) {
+        std::list<NotifyArgs> argsList = process(when, EV_KEY, code, value);
+        argsList += process(when, EV_SYN, SYN_REPORT, 0);
+        return argsList;
+    }
+
+    void testDPadKeyRotation(int32_t originalEvdevCode, int32_t rotatedKeyCode,
+                             ui::LogicalDisplayId displayId = DISPLAY_ID) {
+        std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 1);
         NotifyKeyArgs args = expectSingleKeyArg(argsList);
         ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
         ASSERT_EQ(originalEvdevCode, args.scanCode);
         ASSERT_EQ(rotatedKeyCode, args.keyCode);
         ASSERT_EQ(displayId, args.displayId);
 
-        argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0);
+        argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 0);
         args = expectSingleKeyArg(argsList);
         ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
         ASSERT_EQ(originalEvdevCode, args.scanCode);
@@ -205,23 +211,16 @@
             .With(Args<0>(when))
             .Times(keyCodes.size());
     for (int32_t keyCode : keyCodes) {
-        process(when, EV_KEY, keyCode, 1);
-        process(when, EV_SYN, SYN_REPORT, 0);
-        process(when, EV_KEY, keyCode, 0);
-        process(when, EV_SYN, SYN_REPORT, 0);
+        processKeyAndSync(when, keyCode, 1);
+        processKeyAndSync(when, keyCode, 0);
     }
 }
 
 TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) {
     std::list<NotifyArgs> args;
-    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
-    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
-    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 1);
+    args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 2);
+    args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 0);
 
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
@@ -241,7 +240,7 @@
     ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
 
     // Key down by evdev code.
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
     NotifyKeyArgs args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -255,7 +254,7 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by evdev code.
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
     args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -271,6 +270,7 @@
     // Key down by usage code.
     argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
     argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1);
+    argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -284,8 +284,9 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by usage code.
-    argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+    argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_A);
     argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+    argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
     args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -307,6 +308,7 @@
     // Key down with unknown scan code or usage code.
     std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
     argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+    argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     NotifyKeyArgs args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -320,8 +322,9 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up with unknown scan code or usage code.
-    argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
     argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+    argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
     args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -343,10 +346,12 @@
 
     // Key down
     std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
+    argsList += process(ARBITRARY_TIME, /*readTime=*/12, EV_SYN, SYN_REPORT, 0);
     ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime);
 
     // Key up
-    argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
+    argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 0);
+    argsList += process(ARBITRARY_TIME, /*readTime=*/15, EV_SYN, SYN_REPORT, 0);
     ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime);
 }
 
@@ -360,22 +365,22 @@
     ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
 
     // Metakey down.
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFTSHIFT, 1);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
 
     // Key down.
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 1);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
 
     // Key up.
-    argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 2, KEY_A, 0);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
 
     // Metakey up.
-    argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 3, KEY_LEFTSHIFT, 0);
     ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState);
     ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
 }
@@ -387,11 +392,10 @@
     addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
 
     setDisplayOrientation(ui::Rotation::Rotation90);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
 }
 
 TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) {
@@ -405,43 +409,40 @@
                                                      AINPUT_SOURCE_KEYBOARD);
     setDisplayOrientation(ui::ROTATION_0);
 
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
 
     setDisplayOrientation(ui::ROTATION_90);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_DOWN));
 
     setDisplayOrientation(ui::ROTATION_180);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_RIGHT));
 
     setDisplayOrientation(ui::ROTATION_270);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_UP));
 
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
     setDisplayOrientation(ui::ROTATION_270);
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
     NotifyKeyArgs args = expectSingleKeyArg(argsList);
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
 
     setDisplayOrientation(ui::ROTATION_180);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
     args = expectSingleKeyArg(argsList);
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
@@ -454,9 +455,9 @@
     addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
 
     // Display id should be LogicalDisplayId::INVALID without any display configuration.
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
     ASSERT_GT(argsList.size(), 0u);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
     ASSERT_GT(argsList.size(), 0u);
     ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
 }
@@ -474,9 +475,9 @@
     // ^--- already checked by the previous test
 
     setDisplayOrientation(ui::ROTATION_0);
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
     ASSERT_GT(argsList.size(), 0u);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
     ASSERT_GT(argsList.size(), 0u);
     ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
 
@@ -487,9 +488,9 @@
     argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
                                     InputReaderConfiguration::Change::DISPLAY_INFO);
     ASSERT_EQ(0u, argsList.size());
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
     ASSERT_GT(argsList.size(), 0u);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
     ASSERT_GT(argsList.size(), 0u);
     ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId);
 }
@@ -565,48 +566,48 @@
     ASSERT_FALSE(scrollLockLed);
 
     // Toggle caps lock on.
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(capsLockLed);
     ASSERT_FALSE(numLockLed);
     ASSERT_FALSE(scrollLockLed);
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState());
 
     // Toggle num lock on.
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
     ASSERT_TRUE(capsLockLed);
     ASSERT_TRUE(numLockLed);
     ASSERT_FALSE(scrollLockLed);
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState());
 
     // Toggle caps lock off.
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
     ASSERT_FALSE(capsLockLed);
     ASSERT_TRUE(numLockLed);
     ASSERT_FALSE(scrollLockLed);
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState());
 
     // Toggle scroll lock on.
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(capsLockLed);
     ASSERT_TRUE(numLockLed);
     ASSERT_TRUE(scrollLockLed);
     ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
 
     // Toggle num lock off.
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
     ASSERT_FALSE(capsLockLed);
     ASSERT_FALSE(numLockLed);
     ASSERT_TRUE(scrollLockLed);
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
 
     // Toggle scroll lock off.
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(capsLockLed);
     ASSERT_FALSE(numLockLed);
     ASSERT_FALSE(scrollLockLed);
@@ -618,8 +619,8 @@
     addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
-    // Key down by scan code.
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    // Key down by evdev code.
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
     NotifyKeyArgs args = expectSingleKeyArg(argsList);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -685,37 +686,168 @@
     addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
 
     // Key down
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFT, 1);
     ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE,
               expectSingleKeyArg(argsList).flags);
 }
 
-TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard,
-                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
-                                                      enable_alphabetic_keyboard_wake))) {
-    // For internal alphabetic devices, keys will trigger wake on key down.
+// --- KeyboardInputMapperUnitTest_WakeFlagOverride ---
+
+class KeyboardInputMapperUnitTest_WakeFlagOverride : public KeyboardInputMapperUnitTest {
+protected:
+    virtual void SetUp() override {
+        SetUp(/*wakeFlag=*/com::android::input::flags::enable_alphabetic_keyboard_wake());
+    }
+
+    void SetUp(bool wakeFlag) {
+        mWakeFlagInitialValue = com::android::input::flags::enable_alphabetic_keyboard_wake();
+        com::android::input::flags::enable_alphabetic_keyboard_wake(wakeFlag);
+        KeyboardInputMapperUnitTest::SetUp();
+    }
+
+    void TearDown() override {
+        com::android::input::flags::enable_alphabetic_keyboard_wake(mWakeFlagInitialValue);
+        KeyboardInputMapperUnitTest::TearDown();
+    }
+
+    bool mWakeFlagInitialValue;
+};
+
+// --- KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled
+      : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+    void SetUp() override {
+        KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+    }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled,
+       NonAlphabeticDevice_WakeBehavior) {
+    // For internal non-alphabetic devices keys will not trigger wake.
 
     addKeyByEvdevCode(KEY_A, AKEYCODE_A);
     addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
     addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
 
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1);
-    ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0);
-    ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
-    ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
-    ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
-    ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
-    ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled
+      : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+    void SetUp() override {
+        KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+
+        ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+    }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior) {
+    // For internal alphabetic devices, keys will trigger wake on key down when
+    // flag is enabled.
+    addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+    addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+    addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+    EXPECT_THAT(argsList,
+                ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+    EXPECT_THAT(argsList,
+                ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+    EXPECT_THAT(argsList,
+                ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior_UnknownKey) {
+    // For internal alphabetic devices, unknown keys will trigger wake on key down when
+    // flag is enabled.
+
+    const int32_t USAGE_UNKNOWN = 0x07ffff;
+    EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _))
+            .WillRepeatedly(Return(NAME_NOT_FOUND));
+
+    // Key down with unknown scan code or usage code.
+    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+    EXPECT_THAT(argsList,
+                ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+    // Key up with unknown scan code or usage code.
+    argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticDevice_AlphabeticKeyboardWakeDisabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled
+      : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+    void SetUp() override {
+        KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/false);
+
+        ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+    }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled, WakeBehavior) {
+    // For internal alphabetic devices, keys will not trigger wake when flag is
+    // disabled.
+
+    addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+    addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+    addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+    EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
 }
 
 // --- KeyboardInputMapperTest ---
@@ -730,28 +862,33 @@
     }
     const std::string UNIQUE_ID = "local:0";
 
-    void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
-                             int32_t originalKeyCode, int32_t rotatedKeyCode,
+    void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalEvdevCode,
+                             int32_t rotatedKeyCode,
                              ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
+
+    void processKeyAndSync(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t code,
+                           int32_t value) {
+        process(mapper, when, readTime, EV_KEY, code, value);
+        process(mapper, when, readTime, EV_SYN, SYN_REPORT, 0);
+    }
 };
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
-                                                  int32_t originalScanCode, int32_t originalKeyCode,
-                                                  int32_t rotatedKeyCode,
+                                                  int32_t originalEvdevCode, int32_t rotatedKeyCode,
                                                   ui::LogicalDisplayId displayId) {
     NotifyKeyArgs args;
 
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
-    ASSERT_EQ(originalScanCode, args.scanCode);
+    ASSERT_EQ(originalEvdevCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
     ASSERT_EQ(displayId, args.displayId);
 
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
-    ASSERT_EQ(originalScanCode, args.scanCode);
+    ASSERT_EQ(originalEvdevCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
     ASSERT_EQ(displayId, args.displayId);
 }
@@ -819,23 +956,19 @@
     ASSERT_TRUE(device2->isEnabled());
 
     // Test pad key events
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
     ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
-                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
-                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
-                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+            testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
 
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, newDisplayId));
     ASSERT_NO_FATAL_FAILURE(
-            testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
-                                                AKEYCODE_DPAD_RIGHT, newDisplayId));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
-                                                AKEYCODE_DPAD_DOWN, newDisplayId));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
-                                                AKEYCODE_DPAD_LEFT, newDisplayId));
+            testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, newDisplayId));
 }
 
 TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
@@ -857,20 +990,20 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
@@ -937,16 +1070,16 @@
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
     // Toggle caps lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
 
     // Toggle num lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
 
     // Toggle scroll lock on.
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+    processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     mReader->resetLockedModifierState();
@@ -996,40 +1129,40 @@
     ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
 
     // Toggle num lock on and off.
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
 
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
     ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
 
     // Toggle caps lock on and off.
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
 
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
     ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
 
     // Toggle scroll lock on and off.
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
 
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+    processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
     ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
@@ -1048,10 +1181,10 @@
     KeyboardInputMapper& keyboardMapper =
             constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
-    process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+    processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
     ASSERT_NO_FATAL_FAILURE(
             mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
-    process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+    processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
     ASSERT_NO_FATAL_FAILURE(
             mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
 
@@ -1059,10 +1192,10 @@
     KeyboardInputMapper& dpadMapper =
             constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
     for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
-        process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+        processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
                 WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
-        process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+        processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
                 WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
     }
@@ -1071,10 +1204,10 @@
     KeyboardInputMapper& gamepadMapper =
             constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
     for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
-        process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+        processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
                 WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
-        process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+        processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
                 WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
     }
@@ -1119,22 +1252,22 @@
     addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
     addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
 
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 }
 
@@ -1145,16 +1278,16 @@
     addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
     addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
 
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 }
 
@@ -1169,22 +1302,22 @@
     mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
                                                      AINPUT_SOURCE_KEYBOARD);
 
-    std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_DOWN, 1);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_DOWN, 0);
     ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 
-    argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
     ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
 }
 
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index d8c5eac..b7ca24b 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -19,10 +19,13 @@
 #include "NotifyArgsBuilders.h"
 #include "android/input.h"
 
+#include <vector>
+
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
+#include <input/InputDevice.h>
 #include <input/PrintTools.h>
 #include <inttypes.h>
 #include <linux/input.h>
@@ -51,11 +54,6 @@
     return info;
 }
 
-void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
-    InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID);
-    tracker.setInputDevices({deviceInfo});
-}
-
 const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
 const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE);
 
@@ -120,13 +118,14 @@
     std::unique_ptr<LatencyTracker> mTracker;
     sp<IBinder> connection1;
     sp<IBinder> connection2;
+    std::vector<InputDeviceInfo> inputDevices;
 
     void SetUp() override {
         connection1 = sp<BBinder>::make();
         connection2 = sp<BBinder>::make();
 
-        mTracker = std::make_unique<LatencyTracker>(*this);
-        setDefaultInputDeviceInfo(*mTracker);
+        inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID));
+        mTracker = std::make_unique<LatencyTracker>(*this, inputDevices);
     }
     void TearDown() override {}
 
@@ -140,6 +139,10 @@
      */
     void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines);
 
+    void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) {
+        inputDevices = inputDevicesUpdated;
+    }
+
 private:
     void processTimeline(const InputEventTimeline& timeline) override {
         mReceivedTimelines.push_back(timeline);
@@ -448,7 +451,7 @@
     deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN);
     deviceInfo2.addSource(AINPUT_SOURCE_STYLUS);
 
-    mTracker->setInputDevices({deviceInfo1, deviceInfo2});
+    updateInputDevices({deviceInfo1, deviceInfo2});
     mTracker->trackListener(
             MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
                               AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId)
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1286a36..2b469c5 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -24,6 +24,7 @@
 #include "FakePointerController.h"
 #include "InterfaceMocks.h"
 #include "NotifyArgsBuilders.h"
+#include "ScopedFlagOverride.h"
 #include "TestEventMatchers.h"
 #include "TestInputListener.h"
 
@@ -114,6 +115,10 @@
                 }) {}
 
 class PointerChoreographerTest : public testing::Test {
+public:
+    static constexpr int DENSITY_MEDIUM = 160;
+    static constexpr int DENSITY_HIGH = 320;
+
 protected:
     TestInputListener mTestListener;
     sp<gui::WindowInfosListener> mRegisteredWindowInfoListener;
@@ -124,9 +129,6 @@
                                             mInjectedInitialWindowInfos};
 
     void SetUp() override {
-        // flag overrides
-        input_flags::hide_pointer_indicators_for_secure_windows(true);
-
         ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
             std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
             EXPECT_FALSE(pc->isPointerShown());
@@ -140,6 +142,22 @@
                 });
     }
 
+    void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+        if (input_flags::connected_displays_cursor()) {
+            // setDefaultMouseDisplayId is no-op if connected displays are enabled, mouse display is
+            // set based on primary display of the topology.
+            // Setting topology with the primary display should have same effect as calling
+            // setDefaultMouseDisplayId without topology.
+            // For this reason in tests we mock this behavior by creating topology with a single
+            // display.
+            mChoreographer.setDisplayTopology({.primaryDisplayId = displayId,
+                                               .graph{{displayId, {}}},
+                                               .displaysDensity = {{displayId, DENSITY_MEDIUM}}});
+        } else {
+            mChoreographer.setDefaultMouseDisplayId(displayId);
+        }
+    }
+
     std::shared_ptr<FakePointerController> assertPointerControllerCreated(
             ControllerType expectedType) {
         EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created";
@@ -292,7 +310,7 @@
 
 TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // For a mouse event without a target display, default viewport should be set for
     // the PointerController.
@@ -309,7 +327,7 @@
        WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) {
     // Set one display as a default mouse display and emit mouse event to create PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -320,7 +338,7 @@
 
     // Change default mouse display. Existing PointerController should be removed and a new one
     // should be created.
-    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
     assertPointerControllerRemoved(firstDisplayPc);
 
     auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -329,7 +347,7 @@
 }
 
 TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
@@ -340,8 +358,19 @@
     assertPointerDisplayIdNotified(DISPLAY_ID);
 }
 
+TEST_F(PointerChoreographerTest, NoDefaultMouseSetFallbackToDefaultDisplayId) {
+    mChoreographer.setDisplayViewports(createViewports({ui::LogicalDisplayId::DEFAULT}));
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+    assertPointerControllerCreated(ControllerType::MOUSE);
+
+    assertPointerDisplayIdNotified(ui::LogicalDisplayId::DEFAULT);
+}
+
 TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -354,7 +383,7 @@
 }
 
 TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
@@ -373,7 +402,7 @@
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
 
     // Set one viewport as a default mouse display ID.
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -382,7 +411,7 @@
     assertPointerDisplayIdNotified(DISPLAY_ID);
 
     // Set another viewport as a default mouse display ID. The mouse is moved to the other display.
-    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
     assertPointerControllerRemoved(firstDisplayPc);
 
     assertPointerControllerCreated(ControllerType::MOUSE);
@@ -391,7 +420,7 @@
 
 TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -421,7 +450,7 @@
 
 TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -457,7 +486,7 @@
        AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
     // Add two displays and set one to default.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // Add two devices, one unassociated and the other associated with non-default mouse display.
     mChoreographer.notifyInputDevicesChanged(
@@ -496,7 +525,7 @@
 
 TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -543,7 +572,7 @@
 
 TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -562,7 +591,7 @@
 
 TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // A mouse is connected, and the pointer is shown.
     mChoreographer.notifyInputDevicesChanged(
@@ -599,7 +628,7 @@
 
 TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -628,7 +657,7 @@
 
 TEST_F(PointerChoreographerTest, DisabledMouseConnected) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     InputDeviceInfo mouseDeviceInfo =
             generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
     // Disable this mouse device.
@@ -641,7 +670,7 @@
 
 TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     InputDeviceInfo mouseDeviceInfo =
             generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
 
@@ -660,7 +689,7 @@
 
 TEST_F(PointerChoreographerTest, MultipleEnabledAndDisabledMiceConnectionAndRemoval) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     InputDeviceInfo disabledMouseDeviceInfo =
             generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
     disabledMouseDeviceInfo.setEnabled(false);
@@ -1008,6 +1037,34 @@
     pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
 }
 
+TEST_F(PointerChoreographerTest, StylusHoverEnterFadesMouseOnDisplay) {
+    // Make sure there are PointerControllers for a mouse and a stylus.
+    mChoreographer.setStylusPointerIconEnabled(true);
+    setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
+              generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+    auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_TRUE(mousePc->isPointerShown());
+
+    // Start hovering with a stylus. This should fade the mouse cursor.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    ASSERT_FALSE(mousePc->isPointerShown());
+}
+
 using StylusFixtureParam =
         std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
 
@@ -1378,7 +1435,7 @@
 
 TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // For a touchpad event without a target display, default viewport should be set for
     // the PointerController.
@@ -1394,7 +1451,7 @@
        WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
     // Set one display as a default touchpad display and create PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1403,7 +1460,7 @@
     firstDisplayPc->assertViewportSet(DISPLAY_ID);
 
     // Change default mouse display. Existing PointerController should be removed.
-    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
     assertPointerControllerRemoved(firstDisplayPc);
 
     auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1411,7 +1468,7 @@
 }
 
 TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
@@ -1423,7 +1480,7 @@
 }
 
 TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1436,7 +1493,7 @@
 }
 
 TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) {
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
@@ -1456,7 +1513,7 @@
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
 
     // Set one viewport as a default mouse display ID.
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1466,7 +1523,7 @@
 
     // Set another viewport as a default mouse display ID. ui::LogicalDisplayId::INVALID will be
     // notified before a touchpad event.
-    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
     assertPointerControllerRemoved(firstDisplayPc);
 
     assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1475,7 +1532,7 @@
 
 TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1505,7 +1562,7 @@
 
 TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1582,7 +1639,7 @@
        AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
     // Add two displays and set one to default.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // Add two devices, one unassociated and the other associated with non-default mouse display.
     mChoreographer.notifyInputDevicesChanged(
@@ -1623,7 +1680,7 @@
 
 TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1660,7 +1717,7 @@
 
 TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1680,7 +1737,7 @@
 TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) {
     // Make sure there is a PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1696,7 +1753,7 @@
 TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) {
     // Make sure there is a PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1713,7 +1770,7 @@
 TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) {
     // Make sure there is a PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1730,7 +1787,7 @@
 TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) {
     // Make sure there is a PointerController.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1754,7 +1811,7 @@
 TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) {
     // Make sure there are two PointerControllers on different displays.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -1776,6 +1833,89 @@
     firstMousePc->assertPointerIconNotSet();
 }
 
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    pc->setPosition(100, 200);
+    mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+    EXPECT_CALL(mMockPolicy,
+                filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+                                                    testing::Eq(vec2{10.f, 20.f}),
+                                                    testing::Eq(DISPLAY_ID)))
+            .Times(1)
+            .WillOnce(testing::Return(vec2{4, 13}));
+
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+    pc->assertPosition(104, 213);
+    mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+                                                    WithCursorPosition(104, 213),
+                                                    WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                                     ui::LogicalDisplayId::INVALID)}});
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    pc->setPosition(100, 200);
+    mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+    EXPECT_CALL(mMockPolicy,
+                filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+                                                    testing::Eq(vec2{10.f, 20.f}),
+                                                    testing::Eq(DISPLAY_ID)))
+            .Times(1)
+            .WillOnce(testing::Return(vec2{4, 13}));
+
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(TOUCHPAD_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+    pc->assertPosition(104, 213);
+    mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+                                                    WithCursorPosition(104, 213),
+                                                    WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) {
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+    EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility).Times(0);
+
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+}
+
 using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
         std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
                    std::function<void(PointerChoreographer&)>, int32_t /*action*/>;
@@ -1966,10 +2106,7 @@
     pc->assertSkipScreenshotFlagNotChanged();
 }
 
-TEST_F_WITH_FLAGS(
-        PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
-                                            hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
 
     // Add a first mouse device
@@ -2127,7 +2264,7 @@
 
     // Make sure there are PointerControllers for a mouse and a stylus.
     mChoreographer.setStylusPointerIconEnabled(true);
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2162,7 +2299,7 @@
 TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) {
     // Make sure there are two PointerControllers on different displays.
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2216,7 +2353,7 @@
 
 TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // Hide the pointer on the display, and then connect the mouse.
     mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2233,7 +2370,7 @@
 
 TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // Hide the pointer on the display.
     mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2282,7 +2419,7 @@
 
 TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0,
@@ -2309,7 +2446,7 @@
 
 TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // First drawing tablet is added
     mChoreographer.notifyInputDevicesChanged(
@@ -2357,7 +2494,7 @@
 
 TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
-    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    setDefaultMouseDisplayId(DISPLAY_ID);
 
     // Mouse and drawing tablet connected
     mChoreographer.notifyInputDevicesChanged(
@@ -2601,15 +2738,29 @@
     metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
 }
 
-using PointerChoreographerDisplayTopologyTestFixtureParam =
+class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest {
+protected:
+    DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+                                   ui::Rotation orientation) {
+        DisplayViewport viewport;
+        viewport.displayId = displayId;
+        viewport.logicalRight = width;
+        viewport.logicalBottom = height;
+        viewport.orientation = orientation;
+        return viewport;
+    }
+};
+
+using PointerChoreographerDisplayTopologyCursorTestFixtureParam =
         std::tuple<std::string_view /*name*/, int32_t /*source device*/,
                    ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
                    vec2 /*source position*/, vec2 /*hover move X/Y*/,
                    ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
 
-class PointerChoreographerDisplayTopologyTestFixture
-      : public PointerChoreographerTest,
-        public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
+class PointerChoreographerDisplayTopologyCursorTestFixture
+      : public PointerChoreographerDisplayTopologyTests,
+        public testing::WithParamInterface<
+                PointerChoreographerDisplayTopologyCursorTestFixtureParam> {
 public:
     static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
     static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
@@ -2619,13 +2770,6 @@
     static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
     static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70};
 
-    static constexpr int DENSITY_MEDIUM = 160;
-    static constexpr int DENSITY_HIGH = 320;
-
-    PointerChoreographerDisplayTopologyTestFixture() {
-        com::android::input::flags::connected_displays_cursor(true);
-    }
-
 protected:
     // Note: viewport size is in pixels and offsets in topology are in dp
     std::vector<DisplayViewport> mViewports{
@@ -2658,34 +2802,24 @@
                        {DISPLAY_LEFT_ID, DENSITY_MEDIUM},
                        {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM},
                        {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}};
-
-private:
-    DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
-                                   ui::Rotation orientation) {
-        DisplayViewport viewport;
-        viewport.displayId = displayId;
-        viewport.logicalRight = width;
-        viewport.logicalBottom = height;
-        viewport.orientation = orientation;
-        return viewport;
-    }
 };
 
-TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture,
+       PointerChoreographerDisplayTopologyTest) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
     const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
                  destinationDisplay, destinationPosition] = GetParam();
 
     mChoreographer.setDisplayViewports(mViewports);
-    mChoreographer.setDefaultMouseDisplayId(
-            PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+    setDefaultMouseDisplayId(DISPLAY_CENTER_ID);
     mChoreographer.setDisplayTopology(mTopology);
 
     mChoreographer.notifyInputDevicesChanged(
             {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
 
     auto pc = assertPointerControllerCreated(pointerControllerType);
-    ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-              pc->getDisplayId());
+    ASSERT_EQ(DISPLAY_CENTER_ID, pc->getDisplayId());
 
     // Set initial position of the PointerController.
     pc->setPosition(initialPosition.x, initialPosition.y);
@@ -2717,92 +2851,320 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-        PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+        PointerChoreographerTest, PointerChoreographerDisplayTopologyCursorTestFixture,
         testing::Values(
                 // Note: Upon viewport transition cursor will be positioned at the boundary of the
                 // destination, as we drop any unconsumed delta.
-                std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE,
-                                ControllerType::MOUSE, ToolType::MOUSE,
-                                vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(50, 50) /* destination x/y */),
-                std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
-                                ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
-                                vec2(25, 25) /* delta x/y */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(75, 75) /* destination x/y */),
-                std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE,
-                                ControllerType::MOUSE, ToolType::MOUSE,
-                                vec2(50, 50) /* initial x/y */, vec2(100, 25) /* delta x/y */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
-                                vec2(0,
-                                     50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
+                std::make_tuple(
+                        "PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+                        ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(50, 50) /* destination x/y */),
+                std::make_tuple(
+                        "UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+                        ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+                        vec2(25, 25) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(75, 75) /* destination x/y */),
+                std::make_tuple(
+                        "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+                        ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+                        vec2(100, 25) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_RIGHT_ID,
+                        vec2(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
                 std::make_tuple(
                         "TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
                         ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
                         vec2(-100, 25) /* delta x/y */,
-                        PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_LEFT_ID,
                         vec2(90, 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
-                std::make_tuple("TransitionToTopDisplay",
-                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
-                                ToolType::FINGER, vec2(50, 50) /* initial x/y */,
-                                vec2(25, -100) /* delta x/y */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
-                                vec2(50 + 25 - 50,
-                                     90) /* Bottom edge: (source + delta - offset, height) */),
-                std::make_tuple("TransitionToBottomDisplay",
-                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
-                                ToolType::FINGER, vec2(50, 50) /* initial x/y */,
-                                vec2(25, 100) /* delta x/y */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
-                                vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+                std::make_tuple(
+                        "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                        ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+                        vec2(25, -100) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_TOP_ID,
+                        vec2(50 + 25 - 50,
+                             90) /* Bottom edge: (source + delta - offset, height) */),
+                std::make_tuple(
+                        "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                        ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+                        vec2(25, 100) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_BOTTOM_ID,
+                        vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
                 // move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID
-                std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
-                                ControllerType::MOUSE, ToolType::MOUSE,
-                                vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(35, 0) /* Top edge */),
-                std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
-                                ControllerType::MOUSE, ToolType::MOUSE,
-                                vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(99, 5) /* Top edge */),
-                std::make_tuple("NoTransitionAtBottomOffset",
-                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
-                                ToolType::FINGER, vec2(5, 95) /* initial x/y */,
-                                vec2(0, 100) /* Move Down */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(5, 99) /* Bottom edge */),
-                std::make_tuple("NoTransitionAtLeftOffset",
-                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
-                                ToolType::FINGER, vec2(5, 5) /* initial x/y */,
-                                vec2(-100, 0) /* Move Left */,
-                                PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(0, 5) /* Left edge */),
                 std::make_tuple(
-                        "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
-                        ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
-                        vec2(10, -10) /* Move diagonally to top right corner */,
-                        PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
-                        vec2(0, 90) /* bottom left corner */),
+                        "NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+                        ToolType::MOUSE, vec2(35, 50) /* initial x/y */,
+                        vec2(0, -100) /* Move Up */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(35, 0) /* Top edge */),
                 std::make_tuple(
-                        "TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
-                        ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
-                        vec2(0, -50) /* delta x/y */,
-                        PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID,
-                        /* Bottom edge: ((source + delta - offset) * density, height) */
-                        vec2((20 + 0 + 75) * 2, 200))),
-        [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
-            return std::string{std::get<0>(p.param)};
-        });
+                        "NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+                        ToolType::MOUSE, vec2(95, 5) /* initial x/y */,
+                        vec2(100, 0) /* Move Right */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(99, 5) /* Top edge */),
+                std::make_tuple(
+                        "NoTransitionAtBottomOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                        ControllerType::MOUSE, ToolType::FINGER, vec2(5, 95) /* initial x/y */,
+                        vec2(0, 100) /* Move Down */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(5, 99) /* Bottom edge */),
+                std::make_tuple(
+                        "NoTransitionAtLeftOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                        ControllerType::MOUSE, ToolType::FINGER, vec2(5, 5) /* initial x/y */,
+                        vec2(-100, 0) /* Move Left */,
+                        PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+                        vec2(0, 5) /* Left edge */),
+                std::make_tuple("TransitionAtTopRightCorner",
+                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+                                ToolType::FINGER, vec2(95, 5) /* initial x/y */,
+                                vec2(10, -10) /* Move diagonally to top right corner */,
+                                PointerChoreographerDisplayTopologyCursorTestFixture::
+                                        DISPLAY_TOP_RIGHT_CORNER_ID,
+                                vec2(0, 90) /* bottom left corner */),
+                std::make_tuple("TransitionToHighDpDisplay",
+                                AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+                                ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
+                                vec2(0, -50) /* delta x/y */,
+                                PointerChoreographerDisplayTopologyCursorTestFixture::
+                                        DISPLAY_HIGH_DENSITY_ID,
+                                /* Bottom edge: ((source + delta - offset) * density, height) */
+                                vec2((20 + 0 + 75) * 2, 200))),
+        [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyCursorTestFixtureParam>&
+                   p) { return std::string{std::get<0>(p.param)}; });
+
+class PointerChoreographerDisplayTopologyDefaultMouseDisplayTests
+      : public PointerChoreographerDisplayTopologyTests {
+protected:
+    static constexpr ui::LogicalDisplayId FIRST_DISPLAY_ID = ui::LogicalDisplayId{10};
+    static constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{20};
+    static constexpr ui::LogicalDisplayId THIRD_DISPLAY_ID = ui::LogicalDisplayId{30};
+
+    DisplayViewport createViewport(ui::LogicalDisplayId displayId) {
+        return PointerChoreographerDisplayTopologyTests::createViewport(displayId, /*width=*/100,
+                                                                        /*height=*/100,
+                                                                        ui::ROTATION_0);
+    }
+
+    void setDisplayTopologyWithDisplays(
+            ui::LogicalDisplayId primaryDisplayId,
+            const std::vector<ui::LogicalDisplayId>& adjacentDisplays = {}) {
+        // Prepare a topology with all display connected from left to right.
+        ui::LogicalDisplayId previousDisplay = primaryDisplayId;
+
+        std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>
+                topologyGraph;
+        topologyGraph[primaryDisplayId] = {};
+
+        std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
+        displaysDensity[primaryDisplayId] = DENSITY_MEDIUM;
+
+        for (ui::LogicalDisplayId adjacentDisplayId : adjacentDisplays) {
+            topologyGraph[previousDisplay].push_back({.displayId = adjacentDisplayId,
+                                                      .position = DisplayTopologyPosition::RIGHT,
+                                                      .offsetDp = 0.0f});
+            topologyGraph[adjacentDisplayId].push_back({.displayId = previousDisplay,
+                                                        .position = DisplayTopologyPosition::LEFT,
+                                                        .offsetDp = 0.0f});
+
+            displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM;
+        }
+
+        mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity});
+    }
+};
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       UnrelatedTopologyUpdatesDoNotChangeCursorDisplay) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    // Set first display as primary display and emit mouse event to create PointerController.
+    mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Add another display keeping the primary display unchanged
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Move cursor to second display and add a third display
+    auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(pointerBuilder)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+                                        createViewport(SECOND_DISPLAY_ID),
+                                        createViewport(THIRD_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, /*adjacentDisplays=*/
+                                   {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Change the primary display to the third display
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/
+                                   {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       PrimaryDisplayIsFallbackOnPointerDisplayRemoved) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+    // Add two displays and move cursor to the secondary display
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(pointerBuilder)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Remove the secondary display
+    mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+    assertPointerControllerRemoved(pc);
+    pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       UsePrimaryDisplayIfAssociatedDisplayIsInTopology) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add two displays
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/SECOND_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{FIRST_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, FIRST_DISPLAY_ID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       AllowCrossingDisplayEvenWithAssociatedDisplaySet) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add two displays
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Move cursor to the secondary display
+    auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(pointerBuilder)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       AddAssociatedDisplayCursorOutsideOfDisplayTopology) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add three displays, with only first and second display in DisplayTopolgoy
+    mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+                                        createViewport(SECOND_DISPLAY_ID),
+                                        createViewport(THIRD_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Adds a new mouse associated with third display
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/1, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, THIRD_DISPLAY_ID)}});
+
+    pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(THIRD_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
 
 class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
 
-TEST_F_WITH_FLAGS(
-        PointerChoreographerWindowInfoListenerTest,
-        doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
-                                            hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerWindowInfoListenerTest,
+       doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed) {
     sp<android::gui::WindowInfosListener> registeredListener;
     sp<android::gui::WindowInfosListener> localListenerCopy;
     {
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
index 767f9cd..43975f0 100644
--- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -21,6 +21,14 @@
 static constexpr int32_t MAX_RANDOM_POINTERS = 4;
 static constexpr int32_t MAX_RANDOM_DEVICES = 4;
 
+// The maximum value that we use for the action button field of NotifyMotionArgs. (We allow multiple
+// bits to be set for this since we're just trying to generate a fuzzed event stream that doesn't
+// cause crashes when enum values are converted to Rust — we don't necessarily want it to be valid.)
+//
+// AMOTION_EVENT_BUTTON_STYLUS_SECONDARY should be replaced with whatever AMOTION_EVENT_BUTTON_
+// value is highest if the enum is edited.
+static constexpr int8_t MAX_ACTION_BUTTON_VALUE = (AMOTION_EVENT_BUTTON_STYLUS_SECONDARY << 1) - 1;
+
 int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
     int actionMasked = fdp.PickValueInArray<int>({
             AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
@@ -185,18 +193,16 @@
             fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
     const nsecs_t readTime = downTime;
     const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
+    const int32_t actionButton = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_ACTION_BUTTON_VALUE);
 
     const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
     const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
     return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
-                            POLICY_FLAG_PASS_TO_USER, action,
-                            /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
+                            POLICY_FLAG_PASS_TO_USER, action, actionButton,
                             getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
                             MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
-                            pointerProperties.data(), pointerCoords.data(),
-                            /*xPrecision=*/0,
-                            /*yPrecision=*/0, cursorX, cursorY, downTime,
-                            /*videoFrames=*/{});
+                            pointerProperties.data(), pointerCoords.data(), /*xPrecision=*/0,
+                            /*yPrecision=*/0, cursorX, cursorY, downTime, /*videoFrames=*/{});
 }
 
 } // namespace android
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 157a333..9c027fa 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -17,6 +17,8 @@
 #include <fuzzer/FuzzedDataProvider.h>
 #include <linux/input.h>
 
+#include <vector>
+
 #include "../../InputDeviceMetricsSource.h"
 #include "../InputEventTimeline.h"
 #include "NotifyArgsBuilders.h"
@@ -58,7 +60,8 @@
     FuzzedDataProvider fdp(data, size);
 
     EmptyProcessor emptyProcessor;
-    LatencyTracker tracker(emptyProcessor);
+    std::vector<InputDeviceInfo> emptyDevices;
+    LatencyTracker tracker(emptyProcessor, emptyDevices);
 
     // Make some pre-defined tokens to ensure that some timelines are complete.
     std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 846260a..9a59039 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -34,6 +34,28 @@
                                   android::EventHubInterface::DEVICE_ADDED,
                                   android::EventHubInterface::DEVICE_REMOVED};
 
+static const android::InputDeviceClass kInputDeviceClasses[] = {
+        android::InputDeviceClass::KEYBOARD,
+        android::InputDeviceClass::ALPHAKEY,
+        android::InputDeviceClass::TOUCH,
+        android::InputDeviceClass::CURSOR,
+        android::InputDeviceClass::TOUCH_MT,
+        android::InputDeviceClass::DPAD,
+        android::InputDeviceClass::GAMEPAD,
+        android::InputDeviceClass::SWITCH,
+        android::InputDeviceClass::JOYSTICK,
+        android::InputDeviceClass::VIBRATOR,
+        android::InputDeviceClass::MIC,
+        android::InputDeviceClass::EXTERNAL_STYLUS,
+        android::InputDeviceClass::ROTARY_ENCODER,
+        android::InputDeviceClass::SENSOR,
+        android::InputDeviceClass::BATTERY,
+        android::InputDeviceClass::LIGHT,
+        android::InputDeviceClass::TOUCHPAD,
+        android::InputDeviceClass::VIRTUAL,
+        android::InputDeviceClass::EXTERNAL,
+};
+
 constexpr size_t kValidCodes[] = {
         SYN_REPORT,
         ABS_MT_SLOT,
@@ -105,7 +127,13 @@
     void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
 
     ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
-        return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
+        uint32_t flags = 0;
+        for (auto inputDeviceClass : kInputDeviceClasses) {
+            if (mFdp->ConsumeBool()) {
+                flags |= static_cast<uint32_t>(inputDeviceClass);
+            }
+        }
+        return ftl::Flags<InputDeviceClass>(flags);
     }
     InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
         return mIdentifier;
@@ -332,6 +360,7 @@
                            std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp)
           : mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {}
     ~FuzzInputReaderContext() {}
+    std::string dump() { return "(dump from FuzzInputReaderContext)"; }
     void updateGlobalMetaState() override {}
     int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
     void disableVirtualKeysUntil(nsecs_t time) override {}
@@ -367,8 +396,8 @@
 template <class Fdp>
 InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) {
     InputDeviceIdentifier identifier;
-    identifier.name = fdp.ConsumeRandomLengthString(16);
-    identifier.location = fdp.ConsumeRandomLengthString(12);
+    identifier.name = fdp.ConsumeRandomLengthUtf8String(16);
+    identifier.location = fdp.ConsumeRandomLengthUtf8String(12);
     int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5);
     int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5);
     return InputDevice(context, deviceID, deviceGeneration, identifier);
diff --git a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
index 2f76f18..b258118 100644
--- a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
+++ b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
@@ -15,7 +15,7 @@
  */
 
 #include <fuzzer/FuzzedDataProvider.h>
-
+#include <algorithm>
 /**
  * A thread-safe interface to the FuzzedDataProvider
  */
@@ -60,6 +60,44 @@
         return FuzzedDataProvider::ConsumeRandomLengthString();
     }
 
+    // Converting the string to a UTF-8 string by setting the prefix bits of each
+    // byte according to UTF-8 encoding rules.
+    std::string ConsumeRandomLengthUtf8String(size_t max_length) {
+        std::scoped_lock _l(mLock);
+        std::string result = FuzzedDataProvider::ConsumeRandomLengthString(max_length);
+        size_t remaining_bytes = result.length(), idx = 0;
+        while (remaining_bytes > 0) {
+            size_t random_byte_size = FuzzedDataProvider::ConsumeIntegralInRange(1, 4);
+            size_t byte_size = std::min(random_byte_size, remaining_bytes);
+            switch (byte_size) {
+                // Prefix byte: 0xxxxxxx
+                case 1:
+                    result[idx++] &= 0b01111111;
+                    break;
+                // Prefix bytes: 110xxxxx 10xxxxxx
+                case 2:
+                    result[idx++] = (result[idx] & 0b00011111) | 0b11000000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    break;
+                // Prefix bytes: 1110xxxx 10xxxxxx 10xxxxxx
+                case 3:
+                    result[idx++] = (result[idx] & 0b00001111) | 0b11100000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    break;
+                // Prefix bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+                case 4:
+                    result[idx++] = (result[idx] & 0b00000111) | 0b11110000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+                    break;
+            }
+            remaining_bytes -= byte_size;
+        }
+        return result;
+    }
+
     std::string ConsumeRemainingBytesAsString() {
         std::scoped_lock _l(mLock);
         return FuzzedDataProvider::ConsumeRemainingBytesAsString();
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 4f65e77..78f8f7b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -52,6 +52,7 @@
     ],
 
     whole_static_libs: [
+        "android.adpf.sessionmanager_aidl-ndk",
         "android.os.hintmanager_aidl-ndk",
     ],
 
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 0ba1909..a817a7b 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -173,6 +173,21 @@
     return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo"));
 }
 
+HalResult<void> PowerHalController::sendCompositionData(
+        const std::vector<hal::CompositionData>& data) {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    return CACHE_SUPPORT(6,
+                         processHalResult(handle->sendCompositionData(data),
+                                          "sendCompositionData"));
+}
+
+HalResult<void> PowerHalController::sendCompositionUpdate(const hal::CompositionUpdate& update) {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    return CACHE_SUPPORT(6,
+                         processHalResult(handle->sendCompositionUpdate(update),
+                                          "sendCompositionUpdate"));
+}
+
 } // namespace power
 
 } // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 068c23f..9c83bf5 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -79,6 +79,16 @@
     return HalResult<Aidl::SupportInfo>::unsupported();
 }
 
+HalResult<void> EmptyHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>&) {
+    ALOGV("Skipped sendCompositionData because %s", getUnsupportedMessage());
+    return HalResult<void>::unsupported();
+}
+
+HalResult<void> EmptyHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate&) {
+    ALOGV("Skipped sendCompositionUpdate because %s", getUnsupportedMessage());
+    return HalResult<void>::unsupported();
+}
+
 const char* EmptyHalWrapper::getUnsupportedMessage() {
     return "Power HAL is not supported";
 }
@@ -292,6 +302,14 @@
     return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support));
 }
 
+HalResult<void> AidlHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>& data) {
+    return HalResult<void>::fromStatus(mHandle->sendCompositionData(data));
+}
+
+HalResult<void> AidlHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate& update) {
+    return HalResult<void>::fromStatus(mHandle->sendCompositionUpdate(update));
+}
+
 const char* AidlHalWrapper::getUnsupportedMessage() {
     return "Power HAL doesn't support it";
 }
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 682d1f4..1c53496 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -26,9 +26,9 @@
 #include <utils/Log.h>
 
 #include <unistd.h>
+#include <memory>
 #include <thread>
 
-
 using android::binder::Status;
 
 using namespace aidl::android::hardware::power;
@@ -347,3 +347,50 @@
     result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
     ASSERT_TRUE(result.isUnsupported());
 }
+
+TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionData) {
+    int32_t tgid = 999;
+    int32_t uid = 1001;
+    std::vector<hal::CompositionData> dataOut;
+    dataOut.emplace_back(hal::CompositionData{
+            .timestampNanos = 0L,
+            .scheduledPresentTimestampsNanos = {100},
+            .latchTimestampNanos = 50,
+            .outputIds = {0},
+    });
+    dataOut.emplace_back(hal::CompositionData{
+            .timestampNanos = 200L,
+            .scheduledPresentTimestampsNanos = {300},
+            .latchTimestampNanos = 250,
+            .outputIds = {0},
+    });
+    EXPECT_CALL(*mMockHal.get(), sendCompositionData(_))
+            .Times(Exactly(1))
+            .WillOnce([&](const std::vector<hal::CompositionData>& passedData) {
+                if (!std::equal(passedData.begin(), passedData.end(), dataOut.begin())) {
+                    ADD_FAILURE() << "Passed composition data not the same";
+                }
+                return ndk::ScopedAStatus::ok();
+            });
+
+    ASSERT_TRUE(mWrapper->sendCompositionData(dataOut).isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionUpdate) {
+    int32_t tgid = 999;
+    int32_t uid = 1001;
+    hal::CompositionUpdate dataOut{
+            .timestampNanos = 123,
+            .deadOutputIds = {1, 2, 3},
+    };
+    EXPECT_CALL(*mMockHal.get(), sendCompositionUpdate(_))
+            .Times(Exactly(1))
+            .WillOnce([&](const hal::CompositionUpdate& passedData) {
+                if (passedData != dataOut) {
+                    ADD_FAILURE() << "Passed composition update data not the same";
+                }
+                return ndk::ScopedAStatus::ok();
+            });
+
+    ASSERT_TRUE(mWrapper->sendCompositionUpdate(dataOut).isOk());
+}
\ No newline at end of file
diff --git a/services/sensorservice/sensorservice_flags.aconfig b/services/sensorservice/sensorservice_flags.aconfig
index 452b428..6308973 100644
--- a/services/sensorservice/sensorservice_flags.aconfig
+++ b/services/sensorservice/sensorservice_flags.aconfig
@@ -37,4 +37,14 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+flag {
+  name: "disable_vndk_forged_name"
+  namespace: "sensors"
+  description: "When this flag is enabled, sensor manager will not use forged name to determine if an access is from VNDK"
+  bug: "398253250"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index f698515..d588017 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -29,10 +29,15 @@
         "libexpresslog",
         "libhidlbase",
         "liblog",
-        "libstatslog",
         "libstatssocket",
         "libutils",
     ],
+    generated_sources: [
+        "statslog_hidl.cpp",
+    ],
+    generated_headers: [
+        "statslog_hidl.h",
+    ],
     export_include_dirs: [
         "include/",
     ],
@@ -47,3 +52,28 @@
         "android.frameworks.stats-service.xml",
     ],
 }
+
+genrule {
+    name: "statslog_hidl.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen)" +
+        " --header $(genDir)/statslog_hidl.h" +
+        " --module statshidl" +
+        " --namespace android,util,statshidl",
+    out: [
+        "statslog_hidl.h",
+    ],
+}
+
+genrule {
+    name: "statslog_hidl.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen)" +
+        " --cpp $(genDir)/statslog_hidl.cpp" +
+        " --module statshidl" +
+        " --namespace android,util,statshidl" +
+        " --importHeader statslog_hidl.h",
+    out: [
+        "statslog_hidl.cpp",
+    ],
+}
diff --git a/services/stats/OWNERS b/services/stats/OWNERS
index a61babf..791b711 100644
--- a/services/stats/OWNERS
+++ b/services/stats/OWNERS
@@ -1,9 +1,7 @@
 jeffreyhuang@google.com
 joeo@google.com
-jtnguyen@google.com
 muhammadq@google.com
 ruchirr@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
-yro@google.com
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index b22f903..66f7682 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -26,9 +26,8 @@
 #include <log/log.h>
 #include <stats_annotations.h>
 #include <stats_event.h>
-#include <statslog.h>
 
-#include <unordered_map>
+#include <map>
 
 namespace {
     static const char* g_AtomErrorMetricName =
@@ -118,8 +117,8 @@
         }
     }
 
-    // populate map for quickier access for VendorAtomValue associated annotations by value index
-    std::unordered_map<int, int> fieldIndexToAnnotationSetMap;
+    // populate map for quicker access for VendorAtomValue associated annotations by value index
+    std::map<int, int> fieldIndexToAnnotationSetMap;
     if (vendorAtom.valuesAnnotations) {
         const std::vector<std::optional<AnnotationSet>>& valuesAnnotations =
                 *vendorAtom.valuesAnnotations;
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index 19176d9..0ffa4c3 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -20,7 +20,7 @@
 #include "StatsHal.h"
 
 #include <log/log.h>
-#include <statslog.h>
+#include <statslog_hidl.h>
 
 namespace android {
 namespace frameworks {
@@ -32,24 +32,27 @@
 }
 
 hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) {
-    android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
-                               speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+    android::util::statshidl::stats_write(android::util::statshidl::SPEAKER_IMPEDANCE_REPORTED,
+                                          speakerImpedance.speakerLocation,
+                                          speakerImpedance.milliOhms);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
-    android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
-                               hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+    android::util::statshidl::stats_write(
+            android::util::statshidl::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
+            hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportPhysicalDropDetected(
         const PhysicalDropDetected& physicalDropDetected) {
-    android::util::stats_write(
-            android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg),
-            physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration);
+    android::util::statshidl::stats_write(android::util::statshidl::PHYSICAL_DROP_DETECTED,
+                                          int32_t(physicalDropDetected.confidencePctg),
+                                          physicalDropDetected.accelPeak,
+                                          physicalDropDetected.freefallDuration);
 
     return hardware::Void();
 }
@@ -60,19 +63,19 @@
     for (int i = 0; i < 10 - initialSize; i++) {
         buckets.push_back(0);  // Push 0 for buckets that do not exist.
     }
-    android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
-                               buckets[2], buckets[3], buckets[4], buckets[5], buckets[6],
-                               buckets[7], buckets[8], buckets[9]);
+    android::util::statshidl::stats_write(
+            android::util::statshidl::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], buckets[2],
+            buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], buckets[9]);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
         const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
-    android::util::stats_write(
-            android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type),
-            batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV,
-            batteryHealthSnapshotArgs.currentMicroA,
+    android::util::statshidl::stats_write(
+            android::util::statshidl::BATTERY_HEALTH_SNAPSHOT,
+            int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
+            batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
             batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
             batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
 
@@ -80,23 +83,24 @@
 }
 
 hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) {
-    android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
+    android::util::statshidl::stats_write(android::util::statshidl::SLOW_IO,
+                                          int32_t(slowIo.operation), slowIo.count);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
         const BatteryCausedShutdown& batteryCausedShutdown) {
-    android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
-                               batteryCausedShutdown.voltageMicroV);
+    android::util::statshidl::stats_write(android::util::statshidl::BATTERY_CAUSED_SHUTDOWN,
+                                          batteryCausedShutdown.voltageMicroV);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
         const UsbPortOverheatEvent& usbPortOverheatEvent) {
-    android::util::stats_write(
-            android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+    android::util::statshidl::stats_write(
+            android::util::statshidl::USB_PORT_OVERHEAT_EVENT_REPORTED,
             usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
             usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
             usbPortOverheatEvent.timeToInactive);
@@ -105,9 +109,10 @@
 }
 
 hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) {
-    android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
-                               speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
-                               speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+    android::util::statshidl::stats_write(
+            android::util::statshidl::SPEECH_DSP_STAT_REPORTED, speechDspStat.totalUptimeMillis,
+            speechDspStat.totalDowntimeMillis, speechDspStat.totalCrashCount,
+            speechDspStat.totalRecoverCount);
 
     return hardware::Void();
 }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 252adaa..2c0a66f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -34,7 +34,7 @@
  * A parameter object for creating Display instances
  */
 struct DisplayCreationArgs {
-    DisplayId id;
+    DisplayIdVariant idVariant;
 
     // Size of the display in pixels
     ui::Size pixels = ui::kInvalidSize;
@@ -68,8 +68,8 @@
 public:
     DisplayCreationArgs build() { return std::move(mArgs); }
 
-    DisplayCreationArgsBuilder& setId(DisplayId id) {
-        mArgs.id = id;
+    DisplayCreationArgsBuilder& setId(DisplayIdVariant idVariant) {
+        mArgs.idVariant = idVariant;
         return *this;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 780758e..e2ea0f1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -167,6 +167,8 @@
     // Checks if the buffer's release fence has been set
     virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
 
+    virtual void setReleasedBuffer(sp<GraphicBuffer> buffer) = 0;
+
     // Indicates that the picture profile request was applied to this layer.
     virtual void onPictureProfileCommitted() = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index bda7856..4266da4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -170,6 +170,7 @@
 
     // Returns the DisplayId the output represents, if it has one
     virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
+    virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const = 0;
 
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index c1b864d..c0243b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -145,7 +145,7 @@
 
     // Applies a HWC device layer lut
     virtual void applyDeviceLayerLut(
-            ndk::ScopedFileDescriptor,
+            ::android::base::unique_fd,
             std::vector<std::pair<
                     int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 5519aaf..ec87acc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -46,6 +46,7 @@
 
     // compositionengine::Output overrides
     ftl::Optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
     bool isValid() const override;
     void dump(std::string&) const override;
     using compositionengine::impl::Output::setReleasedLayers;
@@ -104,8 +105,11 @@
             override;
     bool hasPictureProcessing() const override;
     int32_t getMaxLayerPictureProfiles() const override;
+    bool isGpuVirtualDisplay() const {
+        return std::holds_alternative<GpuVirtualDisplayId>(mIdVariant);
+    }
 
-    DisplayId mId;
+    DisplayIdVariant mIdVariant;
     bool mIsDisconnected = false;
     adpf::PowerAdvisor* mPowerAdvisor = nullptr;
     bool mHasPictureProcessing = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 0ccdd22..873764b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -45,6 +45,7 @@
     // compositionengine::Output overrides
     bool isValid() const override;
     ftl::Optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
     void setLayerCachingTexturePoolEnabled(bool) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index a1434f2..efddc85 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -68,7 +68,7 @@
             aidl::android::hardware::graphics::composer3::Composition) override;
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
-    void applyDeviceLayerLut(ndk::ScopedFileDescriptor,
+    void applyDeviceLayerLut(::android::base::unique_fd,
                              std::vector<std::pair<int, LutProperties>>) override;
     bool needsFiltering() const override;
     std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
@@ -104,7 +104,7 @@
     void detectDisallowedCompositionTypeChange(
             aidl::android::hardware::graphics::composer3::Composition from,
             aidl::android::hardware::graphics::composer3::Composition to) const;
-    bool isClientCompositionForced(bool isPeekingThrough, bool isCached) const;
+    bool isClientCompositionForced(bool isPeekingThrough) const;
     void updateLuts(const LayerFECompositionState&,
                     const std::optional<std::vector<std::optional<LutProperties>>>& properties);
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index f934cb2..e42b9b1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -253,7 +253,6 @@
     std::unordered_map<size_t, size_t> mFinalLayerCounts;
     size_t mCachedSetCreationCount = 0;
     size_t mCachedSetCreationCost = 0;
-    std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
 };
 
 } // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index d2a5a20..f65a908 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -52,6 +52,7 @@
 
     MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
     MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
+    MOCK_METHOD1(setReleasedBuffer, void(sp<GraphicBuffer>));
     MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index f2c265a..eaa3dd3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -35,6 +35,7 @@
 
     MOCK_CONST_METHOD0(isValid, bool());
     MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
+    MOCK_CONST_METHOD0(getDisplayIdVariant, ftl::Optional<DisplayIdVariant>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 09c47f0..be36db6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -60,7 +60,7 @@
     MOCK_CONST_METHOD0(needsFiltering, bool());
     MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
     MOCK_METHOD(void, applyDeviceLayerLut,
-                (ndk::ScopedFileDescriptor,
+                (::android::base::unique_fd,
                  (std::vector<std::pair<
                           int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
     MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index d9018bc..dc84195 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -38,7 +38,8 @@
             lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
             lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
             lhs.stretchEffect == rhs.stretchEffect &&
-            lhs.edgeExtensionEffect == rhs.edgeExtensionEffect;
+            lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
+            lhs.whitePointNits == rhs.whitePointNits;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 989f8e3..ab2a03c 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -91,13 +91,13 @@
 
 namespace {
 void offloadOutputs(Outputs& outputs) {
-    if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) {
+    if (outputs.size() < 2) {
         return;
     }
 
     ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload;
     for (const auto& output : outputs) {
-        if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) {
+        if (!output->getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
             // Not HWC-enabled, so it is always client-composited. No need to offload.
             continue;
         }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index e37ce0a..5a54677 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -52,7 +52,7 @@
 Display::~Display() = default;
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
-    mId = args.id;
+    mIdVariant = args.idVariant;
     mPowerAdvisor = args.powerAdvisor;
     mHasPictureProcessing = args.hasPictureProcessing;
     mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
@@ -67,7 +67,7 @@
 }
 
 DisplayId Display::getId() const {
-    return mId;
+    return asDisplayId(mIdVariant);
 }
 
 bool Display::isSecure() const {
@@ -79,11 +79,15 @@
 }
 
 bool Display::isVirtual() const {
-    return mId.isVirtual();
+    return !std::holds_alternative<PhysicalDisplayId>(mIdVariant);
 }
 
 ftl::Optional<DisplayId> Display::getDisplayId() const {
-    return mId;
+    return getId();
+}
+
+ftl::Optional<DisplayIdVariant> Display::getDisplayIdVariant() const {
+    return mIdVariant;
 }
 
 void Display::disconnect() {
@@ -93,14 +97,14 @@
 
     mIsDisconnected = true;
 
-    if (const auto id = HalDisplayId::tryCast(mId)) {
+    if (const auto id = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         getCompositionEngine().getHwComposer().disconnectDisplay(*id);
     }
 }
 
 void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
     Output::setColorTransform(args);
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
         return;
     }
@@ -108,7 +112,7 @@
     auto& hwc = getCompositionEngine().getHwComposer();
     status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
-             to_string(mId).c_str(), result);
+             to_string(*halDisplayId).c_str(), result);
 }
 
 void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -125,7 +129,7 @@
 
     Output::setColorProfile(colorProfile);
 
-    const auto physicalId = PhysicalDisplayId::tryCast(mId);
+    const auto physicalId = getDisplayIdVariant().and_then(asPhysicalDisplayId);
     LOG_FATAL_IF(!physicalId);
     getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
                                                               colorProfile.renderIntent);
@@ -133,7 +137,7 @@
 
 void Display::dump(std::string& out) const {
     const char* const type = isVirtual() ? "virtual" : "physical";
-    base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(mId).c_str(), type,
+    base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(getId()).c_str(), type,
                         getName().c_str());
 
     out.append("\n   Composition Display State:\n");
@@ -157,7 +161,7 @@
         const sp<compositionengine::LayerFE>& layerFE) const {
     auto outputLayer = impl::createOutputLayer(*this, layerFE);
 
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
         outputLayer && !mIsDisconnected && halDisplayId) {
         auto& hwc = getCompositionEngine().getHwComposer();
         auto hwcLayer = hwc.createLayer(*halDisplayId);
@@ -171,8 +175,7 @@
 void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     Output::setReleasedLayers(refreshArgs);
 
-    if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
-        refreshArgs.layersWithQueuedFrames.empty()) {
+    if (mIsDisconnected || isGpuVirtualDisplay() || refreshArgs.layersWithQueuedFrames.empty()) {
         return;
     }
 
@@ -208,7 +211,7 @@
     if (!getState().displayBrightness) {
         return;
     }
-    if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
+    if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
                                                    getState().displayBrightnessNits,
@@ -226,7 +229,7 @@
     Output::beginFrame();
 
     // If we don't have a HWC display, then we are done.
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (!halDisplayId) {
         return;
     }
@@ -244,7 +247,7 @@
     }
 
     // If we don't have a HWC display, then we are done.
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (!halDisplayId) {
         return false;
     }
@@ -266,9 +269,9 @@
     }
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
-        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
-            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+        mPowerAdvisor->setHwcValidateTiming(getId(), hwcValidateStartTime, TimePoint::now());
+        if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
+            mPowerAdvisor->setSkippedValidate(*halDisplayId, hwc.getValidateSkipped(*halDisplayId));
         }
     }
 
@@ -292,7 +295,7 @@
 
 bool Display::getSkipColorTransform() const {
     auto& hwc = getCompositionEngine().getHwComposer();
-    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         return hwc.hasDisplayCapability(*halDisplayId,
                                         DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
@@ -373,7 +376,7 @@
 
         if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
             if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
-                layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()),
+                layer->applyDeviceLayerLut(::android::base::unique_fd(mapperIt->second.release()),
                                            lutsIt->second);
             }
         }
@@ -383,7 +386,7 @@
 }
 
 void Display::executeCommands() {
-    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayIdOpt) {
         return;
     }
@@ -394,7 +397,7 @@
 compositionengine::Output::FrameFences Display::presentFrame() {
     auto fences = impl::Output::presentFrame();
 
-    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayIdOpt) {
         return fences;
     }
@@ -404,13 +407,13 @@
     const TimePoint startTime = TimePoint::now();
 
     if (isPowerHintSessionEnabled() && getState().earliestPresentTime) {
-        mPowerAdvisor->setHwcPresentDelayedTime(mId, *getState().earliestPresentTime);
+        mPowerAdvisor->setHwcPresentDelayedTime(*halDisplayIdOpt, *getState().earliestPresentTime);
     }
 
     hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now());
+        mPowerAdvisor->setHwcPresentTiming(*halDisplayIdOpt, startTime, TimePoint::now());
     }
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
@@ -433,8 +436,8 @@
 void Display::setExpensiveRenderingExpected(bool enabled) {
     Output::setExpensiveRenderingExpected(enabled);
 
-    if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
-        mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
+    if (mPowerAdvisor && !isGpuVirtualDisplay()) {
+        mPowerAdvisor->setExpensiveRenderingExpected(getId(), enabled);
     }
 }
 
@@ -449,15 +452,15 @@
 // For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with
 // fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence.
 void Display::setHintSessionGpuStart(TimePoint startTime) {
-    mPowerAdvisor->setGpuStartTime(mId, startTime);
+    mPowerAdvisor->setGpuStartTime(getId(), startTime);
 }
 
 void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
-    mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+    mPowerAdvisor->setGpuFenceTime(getId(), std::move(gpuFence));
 }
 
 void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) {
-    mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
+    mPowerAdvisor->setRequiresRenderEngine(getId(), requiresRenderEngine);
 }
 
 const aidl::android::hardware::graphics::composer3::OverlayProperties*
@@ -478,7 +481,7 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
+    if (isGpuVirtualDisplay() && !mustRecompose()) {
         ALOGV("Skipping display composition");
         return;
     }
@@ -487,7 +490,7 @@
 }
 
 bool Display::supportsOffloadPresent() const {
-    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index de1d13a..00a61a5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -118,6 +118,10 @@
     return {};
 }
 
+ftl::Optional<DisplayIdVariant> Output::getDisplayIdVariant() const {
+    return {};
+}
+
 const std::string& Output::getName() const {
     return mName;
 }
@@ -436,8 +440,8 @@
 ftl::Future<std::monostate> Output::present(
         const compositionengine::CompositionRefreshArgs& refreshArgs) {
     const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
-        return getDisplayId()
-                .and_then(PhysicalDisplayId::tryCast)
+        return getDisplayIdVariant()
+                .and_then(asPhysicalDisplayId)
                 .and_then([&refreshArgs](PhysicalDisplayId id) {
                     return refreshArgs.frameTargets.get(id);
                 })
@@ -890,8 +894,8 @@
         return;
     }
 
-    if (auto frameTargetPtrOpt = getDisplayId()
-                                         .and_then(PhysicalDisplayId::tryCast)
+    if (auto frameTargetPtrOpt = getDisplayIdVariant()
+                                         .and_then(asPhysicalDisplayId)
                                          .and_then([&refreshArgs](PhysicalDisplayId id) {
                                              return refreshArgs.frameTargets.get(id);
                                          })) {
@@ -1389,7 +1393,8 @@
     // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
     // GPU composition can finish in time. We must reset GPU frequency afterwards,
     // because high frequency consumes extra battery.
-    const bool expensiveRenderingExpected =
+    const bool expensiveBlurs = mLayerRequestingBackgroundBlur != nullptr;
+    const bool expensiveRenderingExpected = expensiveBlurs ||
             std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
                         [outputDataspace =
                                  clientCompositionDisplay.outputDataspace](const auto& layer) {
@@ -1671,6 +1676,7 @@
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
         layer->getLayerFE().setReleaseFence(releaseFence);
+        layer->getLayerFE().setReleasedBuffer(layer->getLayerFE().getCompositionState()->buffer);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1840,7 +1846,7 @@
     if (!getDisplayId()) {
         return;
     }
-    if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+    if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         const status_t error =
                 hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index d89b52d..ea36011 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -369,8 +369,11 @@
                                                       layerFEState->buffer->getPixelFormat()))
                                             : std::nullopt;
 
-    auto hdrRenderType =
-            getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+    // prefer querying this from gralloc instead to catch 2094-10 metadata
+    const bool hasHdrMetadata = layerFEState->hdrMetadata.validTypes != 0;
+
+    auto hdrRenderType = getHdrRenderType(outputState.dataspace, pixelFormat,
+                                          layerFEState->desiredHdrSdrRatio, hasHdrMetadata);
 
     // Determine the output dependent dataspace for this layer. If it is
     // colorspace agnostic, it just uses the dataspace chosen for the output to
@@ -393,8 +396,8 @@
     }
 
     // re-get HdrRenderType after the dataspace gets changed.
-    hdrRenderType =
-            getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+    hdrRenderType = getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio,
+                                     hasHdrMetadata);
 
     // For hdr content, treat the white point as the display brightness - HDR content should not be
     // boosted or dimmed.
@@ -416,12 +419,20 @@
         state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
         state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
     } else {
+        const bool isLayerFp16 = pixelFormat && *pixelFormat == ui::PixelFormat::RGBA_FP16;
         float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
         // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
         // range that we may need to re-adjust to the current display conditions
+        // Do NOT do this when we may render fp16 to an fp16 client target, to avoid applying
+        // and additional gain to the layer. This is because the fp16 client target should
+        // already be adapted to remap 1.0 to the SDR white point in the panel's luminance
+        // space.
         if (hdrRenderType == HdrRenderType::DISPLAY_HDR) {
-            layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+            if (!FlagManager::getInstance().fp16_client_target() || !isLayerFp16) {
+                layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+            }
         }
+
         state.dimmingRatio =
                 std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
                            1.f);
@@ -619,7 +630,7 @@
                                           lutProperties[i].samplingKey)}});
         }
 
-        luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+        luts.pfd.set(dup(lutFileDescriptor.get()));
         luts.offsets = lutOffsets;
         luts.lutProperties = std::move(aidlProperties);
     }
@@ -865,8 +876,7 @@
                                             bool isPeekingThrough, bool skipLayer) {
     auto& outputDependentState = editState();
 
-    bool isCached = !skipLayer && outputDependentState.overrideInfo.buffer;
-    if (isClientCompositionForced(isPeekingThrough, isCached)) {
+    if (isClientCompositionForced(isPeekingThrough)) {
         // If we are forcing client composition, we need to tell the HWC
         requestedCompositionType = Composition::CLIENT;
     }
@@ -956,12 +966,9 @@
     }
 }
 
-bool OutputLayer::isClientCompositionForced(bool isPeekingThrough, bool isCached) const {
-    // If this layer was flattened into a CachedSet then it is not necessary for
-    // the GPU to compose it.
-    bool requiresClientDrawnRoundedCorners = !isCached && getLayerFE().hasRoundedCorners();
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
     return getState().forceClientComposition ||
-            (!isPeekingThrough && requiresClientDrawnRoundedCorners);
+            (!isPeekingThrough && getLayerFE().hasRoundedCorners());
 }
 
 void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) {
@@ -1006,7 +1013,7 @@
 }
 
 void OutputLayer::applyDeviceLayerLut(
-        ndk::ScopedFileDescriptor lutFileDescriptor,
+        ::android::base::unique_fd lutFd,
         std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) {
     auto& state = editState();
     LOG_FATAL_IF(!state.hwc);
@@ -1025,9 +1032,9 @@
             samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0]));
         }
     }
-    hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()),
-                                                       std::move(offsets), std::move(dimensions),
-                                                       std::move(sizes), std::move(samplingKeys));
+    hwcState.luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), std::move(offsets),
+                                                       std::move(dimensions), std::move(sizes),
+                                                       std::move(samplingKeys));
 }
 
 bool OutputLayer::needsFiltering() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 783209c..2081cd5 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -243,17 +243,9 @@
 
     mCurrentGeometry = hash;
     mLastGeometryUpdate = now;
-
-    for (const CachedSet& cachedSet : mLayers) {
-        if (cachedSet.getLayerCount() > 1) {
-            ++mInvalidatedCachedSetAges[cachedSet.getAge()];
-        }
-    }
-
     mLayers.clear();
 
     if (mNewCachedSet) {
-        ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
         mNewCachedSet = std::nullopt;
     }
 }
@@ -312,7 +304,6 @@
             mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
             if (mNewCachedSet->hasBufferUpdate()) {
                 ALOGV("[%s] Dropping new cached set", __func__);
-                ++mInvalidatedCachedSetAges[0];
                 mNewCachedSet = std::nullopt;
             } else if (mNewCachedSet->hasReadyBuffer()) {
                 ALOGV("[%s] Found ready buffer", __func__);
@@ -325,6 +316,7 @@
                                 priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
                         OutputLayer::CompositionState& state =
                                 (*incomingLayerIter)->getOutputLayer()->editState();
+
                         state.overrideInfo = {
                                 .buffer = mNewCachedSet->getBuffer(),
                                 .acquireFence = mNewCachedSet->getDrawFence(),
@@ -338,10 +330,6 @@
                         };
                         ++incomingLayerIter;
                     }
-
-                    if (currentLayerIter->getLayerCount() > 1) {
-                        ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
-                    }
                     ++currentLayerIter;
 
                     skipCount -= layerCount;
@@ -378,9 +366,9 @@
                 };
                 ++incomingLayerIter;
             }
+            priorBlurLayer = currentLayerIter->getBlurLayer();
         } else if (currentLayerIter->getLayerCount() > 1) {
             // Break the current layer into its constituent layers
-            ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
             for (CachedSet& layer : currentLayerIter->decompose()) {
                 bool disableBlur =
                         priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
@@ -400,8 +388,8 @@
             currentLayerIter->updateAge(now);
             merged.emplace_back(*currentLayerIter);
             ++incomingLayerIter;
+          priorBlurLayer = currentLayerIter->getBlurLayer();
         }
-        priorBlurLayer = currentLayerIter->getBlurLayer();
         ++currentLayerIter;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index ad65c44..34c09db 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -43,6 +43,10 @@
 using ::testing::SaveArg;
 using ::testing::StrictMock;
 
+static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u);
+static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u);
+static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(567u);
+
 struct CompositionEngineTest : public testing::Test {
     std::shared_ptr<TimeStats> mTimeStats;
 
@@ -52,6 +56,31 @@
     std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
     std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
     std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+
+    std::array<impl::OutputCompositionState, 3> mOutputStates;
+
+    void SetUp() override {
+        EXPECT_CALL(*mOutput1, getDisplayId)
+                .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+        EXPECT_CALL(*mOutput1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
+        EXPECT_CALL(*mOutput2, getDisplayId)
+                .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+        EXPECT_CALL(*mOutput2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
+        EXPECT_CALL(*mOutput3, getDisplayId)
+                .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId3)));
+        EXPECT_CALL(*mOutput3, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId3));
+
+        // Most tests will depend on the outputs being enabled.
+        for (auto& state : mOutputStates) {
+            state.isEnabled = true;
+        }
+
+        EXPECT_CALL(*mOutput1, getState).WillRepeatedly(ReturnRef(mOutputStates[0]));
+        EXPECT_CALL(*mOutput2, getState).WillRepeatedly(ReturnRef(mOutputStates[1]));
+        EXPECT_CALL(*mOutput3, getState).WillRepeatedly(ReturnRef(mOutputStates[2]));
+    }
 };
 
 TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
@@ -94,7 +123,7 @@
     StrictMock<CompositionEnginePartialMock> mEngine;
 };
 
-TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+TEST_F(CompositionEnginePresentTest, zeroOutputs) {
     // present() always calls preComposition() and postComposition()
     EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
     EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
@@ -102,7 +131,7 @@
     mEngine.present(mRefreshArgs);
 }
 
-TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+TEST_F(CompositionEnginePresentTest, threeOutputs) {
     // Expect calls to in a certain sequence
     InSequence seq;
 
@@ -114,9 +143,7 @@
     EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
     EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
 
-    // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce
-    // calls to getDisplayId, which are not relevant to this test.
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+    EXPECT_CALL(*mOutput1, supportsOffloadPresent).WillOnce(Return(false));
 
     // The last step is to actually present each output.
     EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)))
@@ -284,8 +311,6 @@
     std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
     std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
 
-    static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u);
-    static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u);
     static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u};
     static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u};
 
@@ -294,12 +319,23 @@
     void SetUp() override {
         EXPECT_CALL(*mDisplay1, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+        EXPECT_CALL(*mDisplay1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
         EXPECT_CALL(*mDisplay2, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+        EXPECT_CALL(*mDisplay2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
         EXPECT_CALL(*mVirtualDisplay, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId)));
+        const DisplayIdVariant gpuVariant =
+                GpuVirtualDisplayId::fromValue(kGpuVirtualDisplayId.value);
+        EXPECT_CALL(*mVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(gpuVariant));
+
         EXPECT_CALL(*mHalVirtualDisplay, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId)));
+        const DisplayIdVariant halVariant =
+                HalVirtualDisplayId::fromValue(kHalVirtualDisplayId.value);
+        EXPECT_CALL(*mHalVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(halVariant));
 
         // Most tests will depend on the outputs being enabled.
         for (auto& state : mOutputStates) {
@@ -332,7 +368,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mDisplay2});
 
     mEngine.present(mRefreshArgs);
@@ -345,7 +380,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mDisplay2});
 
     mEngine.present(mRefreshArgs);
@@ -358,20 +392,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
-    setOutputs({mDisplay1, mDisplay2});
-
-    mEngine.present(mRefreshArgs);
-}
-
-TEST_F(CompositionEngineOffloadTest, dependsOnFlag) {
-    EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0);
-    EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0);
-
-    EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
-    EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
-
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
     setOutputs({mDisplay1, mDisplay2});
 
     mEngine.present(mRefreshArgs);
@@ -382,7 +402,6 @@
 
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1});
 
     mEngine.present(mRefreshArgs);
@@ -397,7 +416,6 @@
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mDisplay2, mVirtualDisplay});
 
     mEngine.present(mRefreshArgs);
@@ -410,7 +428,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mVirtualDisplay});
 
     mEngine.present(mRefreshArgs);
@@ -423,7 +440,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
     EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mHalVirtualDisplay});
 
     mEngine.present(mRefreshArgs);
@@ -440,7 +456,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2});
 
     mEngine.present(mRefreshArgs);
@@ -458,7 +473,6 @@
     EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mDisplay2});
 
     mEngine.present(mRefreshArgs);
@@ -478,7 +492,6 @@
     EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
     EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     setOutputs({mDisplay1, mDisplay2, mHalVirtualDisplay});
 
     mEngine.present(mRefreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c1e59d0..77fd446 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -278,7 +278,7 @@
             impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForGpuVirtualDisplay());
     EXPECT_FALSE(display->isSecure());
     EXPECT_TRUE(display->isVirtual());
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
+    EXPECT_TRUE(display->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
 }
 
 /*
@@ -318,6 +318,7 @@
     EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<HalVirtualDisplayId>));
     EXPECT_FALSE(mDisplay->isValid());
 
     const auto& filter = mDisplay->getState().layerFilter;
@@ -337,6 +338,7 @@
     EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
     EXPECT_FALSE(mDisplay->isValid());
 
     const auto& filter = mDisplay->getState().layerFilter;
@@ -572,7 +574,7 @@
     auto args = getDisplayCreationArgsForGpuVirtualDisplay();
     std::shared_ptr<Display> gpuDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine, args);
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
+    EXPECT_TRUE(gpuDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
 
     chooseCompositionStrategy(gpuDisplay.get());
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09ad9fa..590626a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -148,20 +148,23 @@
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
 
         virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+        virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override {
+            return DisplayIdVariant(mId);
+        }
 
         virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
         virtual int32_t getMaxLayerPictureProfiles() const override {
             return mMaxLayerPictureProfiles;
         }
 
-        void setDisplayIdForTest(DisplayId value) { mId = value; }
+        void setDisplayIdForTest(PhysicalDisplayId value) { mId = value; }
 
         void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
 
         void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
 
     private:
-        ftl::Optional<DisplayId> mId;
+        PhysicalDisplayId mId;
         bool mHasPictureProcessing;
         int32_t mMaxLayerPictureProfiles;
     };
@@ -3311,6 +3314,17 @@
     sp<Fence> layer2Fence = sp<Fence>::make();
     sp<Fence> layer3Fence = sp<Fence>::make();
 
+    // Set up layerfe buffers
+    LayerFECompositionState layer1State;
+    layer1State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer2State;
+    layer2State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer3State;
+    layer3State.buffer = nullptr;
+    EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+    EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+    EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
     Output::FrameFences frameFences;
     frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
@@ -3327,14 +3341,23 @@
             .WillOnce([&layer1Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer1Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer1State.buffer, buffer);
+    });
     EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_))
             .WillOnce([&layer2Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer2Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer2State.buffer, buffer);
+    });
     EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_))
             .WillOnce([&layer3Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer3State.buffer, buffer);
+    });
 
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
@@ -3350,6 +3373,17 @@
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
     frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
 
+    // Set up layerfe buffers
+    LayerFECompositionState layer1State;
+    layer1State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer2State;
+    layer2State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer3State;
+    layer3State.buffer = nullptr;
+    EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+    EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+    EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
     EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
@@ -3359,6 +3393,15 @@
     EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
+    EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer1State.buffer, buffer);
+    });
+    EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer2State.buffer, buffer);
+    });
+    EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer3State.buffer, buffer);
+    });
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
@@ -3401,7 +3444,6 @@
             .WillOnce([&presentFence](FenceResult fenceResult) {
                 EXPECT_EQ(FenceResult(presentFence), fenceResult);
             });
-
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index a086aee..87a677c 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -97,9 +97,7 @@
             const bool force = desiredModeOpt->force;
             desiredModeOpt = std::move(desiredMode);
             desiredModeOpt->emitEvent |= emitEvent;
-            if (FlagManager::getInstance().connected_display()) {
-                desiredModeOpt->force |= force;
-            }
+            desiredModeOpt->force |= force;
             return DesiredModeAction::None;
         }
 
@@ -191,7 +189,7 @@
     // cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
     // consumed at this point, so clear the `force` flag to prevent an endless loop of
     // `initiateModeChange`.
-    if (FlagManager::getInstance().connected_display()) {
+    {
         std::scoped_lock lock(displayPtr->desiredModeLock);
         if (displayPtr->desiredModeOpt) {
             displayPtr->desiredModeOpt->force = false;
diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
index c68020c..71d9f2e 100644
--- a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
+++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
@@ -35,6 +35,7 @@
 
     VirtualDisplayId displayId() const { return mVirtualId; }
     bool isGpu() const { return mIsGpu; }
+    const std::string& uniqueId() const { return mUniqueId; }
 
     void dump(utils::Dumper& dumper) const {
         using namespace std::string_view_literals;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index e8b09b0..de7d455 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -26,7 +26,6 @@
 
 #include <common/trace.h>
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/DisplayColorProfileCreationArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
@@ -169,8 +168,7 @@
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
-    // TODO(b/241285876): Skip this for virtual displays.
-    if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+    if (!isVirtual() && (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON)) {
         if (mStagedBrightness && mBrightness != mStagedBrightness) {
             getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
             mBrightness = *mStagedBrightness;
@@ -283,6 +281,7 @@
 
     dumper.dump("name"sv, '"' + mDisplayName + '"');
     dumper.dump("powerMode"sv, mPowerMode);
+    dumper.dump("optimizationPolicy"sv, mOptimizationPolicy);
 
     if (mRefreshRateSelector) {
         mRefreshRateSelector->dump(dumper);
@@ -297,6 +296,10 @@
     return mCompositionDisplay->getId();
 }
 
+bool DisplayDevice::isVirtual() const {
+    return mCompositionDisplay->isVirtual();
+}
+
 bool DisplayDevice::isSecure() const {
     return mCompositionDisplay->isSecure();
 }
@@ -305,6 +308,15 @@
     mCompositionDisplay->setSecure(secure);
 }
 
+gui::ISurfaceComposer::OptimizationPolicy DisplayDevice::getOptimizationPolicy() const {
+    return mOptimizationPolicy;
+}
+
+void DisplayDevice::setOptimizationPolicy(
+        gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+    mOptimizationPolicy = optimizationPolicy;
+}
+
 const Rect DisplayDevice::getBounds() const {
     return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b5a543a..1b14145 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -23,6 +23,8 @@
 #include <android-base/thread_annotations.h>
 #include <android/native_window.h>
 #include <binder/IBinder.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
 #include <gui/LayerState.h>
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
@@ -61,15 +63,21 @@
 struct CompositionInfo;
 struct DisplayDeviceCreationArgs;
 
-namespace compositionengine {
-class Display;
-class DisplaySurface;
-} // namespace compositionengine
-
 namespace display {
 class DisplaySnapshot;
 } // namespace display
 
+namespace gui {
+inline const char* to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+    switch (optimizationPolicy) {
+        case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
+            return "optimizeForPower";
+        case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
+            return "optimizeForPerformance";
+    }
+}
+} // namespace gui
+
 class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
@@ -85,7 +93,7 @@
         return mCompositionDisplay;
     }
 
-    bool isVirtual() const { return getId().isVirtual(); }
+    bool isVirtual() const;
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
@@ -93,6 +101,11 @@
     bool isSecure() const;
     void setSecure(bool secure);
 
+    // The optimization policy influences whether this display is optimized for power or
+    // performance.
+    gui::ISurfaceComposer::OptimizationPolicy getOptimizationPolicy() const;
+    void setOptimizationPolicy(gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
+
     int getWidth() const;
     int getHeight() const;
     ui::Size getSize() const { return {getWidth(), getHeight()}; }
@@ -118,17 +131,30 @@
 
     DisplayId getId() const;
 
+    DisplayIdVariant getDisplayIdVariant() const {
+        const auto idVariant = mCompositionDisplay->getDisplayIdVariant();
+        LOG_FATAL_IF(!idVariant);
+        return *idVariant;
+    }
+
+    std::optional<VirtualDisplayIdVariant> getVirtualDisplayIdVariant() const {
+        return ftl::match(
+                getDisplayIdVariant(),
+                [](PhysicalDisplayId) { return std::optional<VirtualDisplayIdVariant>(); },
+                [](auto id) { return std::optional<VirtualDisplayIdVariant>(id); });
+    }
+
     // Shorthand to upcast the ID of a display whose type is known as a precondition.
     PhysicalDisplayId getPhysicalId() const {
-        const auto id = PhysicalDisplayId::tryCast(getId());
-        LOG_FATAL_IF(!id);
-        return *id;
+        const auto physicalDisplayId = asPhysicalDisplayId(getDisplayIdVariant());
+        LOG_FATAL_IF(!physicalDisplayId);
+        return *physicalDisplayId;
     }
 
     VirtualDisplayId getVirtualId() const {
-        const auto id = VirtualDisplayId::tryCast(getId());
-        LOG_FATAL_IF(!id);
-        return *id;
+        const auto virtualDisplayId = asVirtualDisplayId(getDisplayIdVariant());
+        LOG_FATAL_IF(!virtualDisplayId);
+        return *virtualDisplayId;
     }
 
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -236,6 +262,9 @@
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
+    gui::ISurfaceComposer::OptimizationPolicy mOptimizationPolicy =
+            gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
+
     uint32_t mFlags = 0;
 
     // Requested refresh rate in fps, supported only for virtual displays.
@@ -283,11 +312,16 @@
     std::string displayName;
     std::string uniqueId;
     bool isSecure = false;
+
+    gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy =
+            gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
     bool isProtected = false;
     // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
     Fps requestedRefreshRate;
     int32_t maxLayerPictureProfiles = 0;
     bool hasPictureProcessing = false;
+    hardware::graphics::composer::hal::PowerMode initialPowerMode{
+            hardware::graphics::composer::hal::PowerMode::OFF};
 
 private:
     static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index bb6bebe..d547af9 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1773,7 +1773,6 @@
 }
 
 bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
-    if (!FlagManager::getInstance().multithreaded_present()) return false;
     const auto displayId = translate<int64_t>(display);
     std::vector<AidlDisplayCapability> capabilities;
     const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 252c6b6..8d16a6b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -439,11 +439,8 @@
     // FIXME (b/319505580): At least the first config set on an external display must be
     // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints`
     // for simplicity.
-    const bool connected_display = FlagManager::getInstance().connected_display();
-
     if (isVsyncPeriodSwitchSupported() &&
-        (!connected_display ||
-         getConnectionType().value_opt() != ui::DisplayConnectionType::External)) {
+        getConnectionType().value_opt() != ui::DisplayConnectionType::External) {
         Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
         hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
         hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
@@ -638,7 +635,7 @@
                                [](int32_t i, LutProperties j) { return std::make_pair(i, j); });
                 outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties);
                 lutFileDescriptorMapper.emplace_or_replace(layer.get(),
-                                                           ndk::ScopedFileDescriptor(
+                                                           ::android::base::unique_fd(
                                                                    layerLut.luts.pfd.release()));
             } else {
                 ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found"
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c3deb84..7c1f8e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -115,7 +115,7 @@
     using LayerLuts =
             ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>;
     using LutFileDescriptorMapper =
-            ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+            ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, kLutFileDescriptorMapperSize>;
 
     [[nodiscard]] virtual hal::Error acceptChanges() = 0;
     [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index db41b9b..14088a6 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -145,7 +145,7 @@
         case HotplugEvent::Disconnected:
             return onHotplugDisconnect(hwcDisplayId);
         case HotplugEvent::LinkUnstable:
-            return {};
+            return onHotplugLinkTrainingFailure(hwcDisplayId);
     }
 }
 
@@ -1068,7 +1068,7 @@
     return mSupportedLayerGenericMetadata;
 }
 
-ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, 20>&
 HWComposer::getLutFileDescriptorMapper() {
     return mLutFileDescriptorMapper;
 }
@@ -1245,6 +1245,16 @@
     return DisplayIdentificationInfo{.id = *displayId};
 }
 
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugLinkTrainingFailure(
+        hal::HWDisplayId hwcDisplayId) {
+    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+    if (!displayId) {
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
+        return {};
+    }
+    return DisplayIdentificationInfo{.id = *displayId};
+}
+
 void HWComposer::loadCapabilities() {
     static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed");
     auto capabilities = mComposer->getCapabilities();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 2c0aa3d..472411c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -544,6 +544,7 @@
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
     std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
+    std::optional<DisplayIdentificationInfo> onHotplugLinkTrainingFailure(hal::HWDisplayId);
     bool shouldIgnoreHotplugConnect(hal::HWDisplayId, uint8_t port,
                                     bool hasDisplayIdentificationData) const;
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 384f7b2..56ed11f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -47,7 +47,8 @@
 
 namespace android {
 
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
+                                             VirtualDisplayIdVariant virtualIdVariant,
                                              const sp<IGraphicBufferProducer>& sink,
                                              const sp<IGraphicBufferProducer>& bqProducer,
                                              const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -58,7 +59,7 @@
       : ConsumerBase(bqConsumer),
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         mHwc(hwc),
-        mDisplayId(displayId),
+        mVirtualIdVariant(virtualIdVariant),
         mDisplayName(name),
         mSource{},
         mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
@@ -119,7 +120,7 @@
 }
 
 status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return NO_ERROR;
     }
 
@@ -133,7 +134,7 @@
 }
 
 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return NO_ERROR;
     }
 
@@ -181,7 +182,10 @@
 }
 
 status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    const auto halVirtualDisplayId = ftl::match(
+            mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+            [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+    if (!halVirtualDisplayId) {
         return NO_ERROR;
     }
 
@@ -212,11 +216,8 @@
     VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
              mOutputProducerSlot, outBuffer.get());
 
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    LOG_FATAL_IF(!halDisplayId);
-    // At this point we know the output buffer acquire fence,
-    // so update HWC state with it.
-    mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
+    // At this point we know the output buffer acquire fence, so update HWC state with it.
+    mHwc.setOutputBuffer(*halVirtualDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
@@ -227,7 +228,7 @@
             hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
         }
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
+        result = mHwc.setClientTarget(*halVirtualDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
                                       ui::Dataspace::UNKNOWN, hdrSdrRatio);
     }
 
@@ -235,8 +236,8 @@
 }
 
 void VirtualDisplaySurface::onFrameCommitted() {
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    if (!halDisplayId) {
+    const auto halDisplayId = asHalDisplayId(mVirtualIdVariant);
+    if (!halDisplayId.has_value()) {
         return;
     }
 
@@ -250,8 +251,7 @@
         Mutex::Autolock lock(mMutex);
         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
         VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
-        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
-                retireFence);
+        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence);
         releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
     }
 
@@ -299,7 +299,7 @@
 
 status_t VirtualDisplaySurface::requestBuffer(int pslot,
         sp<GraphicBuffer>* outBuf) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
@@ -321,7 +321,7 @@
 
 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
         PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
-    LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+    LOG_ALWAYS_FATAL_IF(isBackedByGpu());
 
     status_t result =
             mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -373,7 +373,7 @@
                                               PixelFormat format, uint64_t usage,
                                               uint64_t* outBufferAge,
                                               FrameEventHistoryDelta* outTimestamps) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
                                                    outTimestamps);
     }
@@ -459,7 +459,7 @@
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
         const QueueBufferInput& input, QueueBufferOutput* output) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
@@ -517,7 +517,7 @@
 
 status_t VirtualDisplaySurface::cancelBuffer(int pslot,
         const sp<Fence>& fence) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
@@ -621,7 +621,10 @@
 }
 
 status_t VirtualDisplaySurface::refreshOutputBuffer() {
-    LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+    const auto halVirtualDisplayId = ftl::match(
+            mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+            [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+    LOG_ALWAYS_FATAL_IF(!halVirtualDisplayId);
 
     if (mOutputProducerSlot >= 0) {
         mSource[SOURCE_SINK]->cancelBuffer(
@@ -640,14 +643,16 @@
     // until after GPU calls queueBuffer(). So here we just set the buffer
     // (for use in HWC prepare) but not the fence; we'll call this again with
     // the proper fence once we have it.
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    LOG_FATAL_IF(!halDisplayId);
-    result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
+    result = mHwc.setOutputBuffer(*halVirtualDisplayId, Fence::NO_FENCE,
                                   mProducerBuffers[mOutputProducerSlot]);
 
     return result;
 }
 
+bool VirtualDisplaySurface::isBackedByGpu() const {
+    return std::holds_alternative<GpuVirtualDisplayId>(mVirtualIdVariant);
+}
+
 // This slot mapping function is its own inverse, so two copies are unnecessary.
 // Both are kept to make the intent clear where the function is called, and for
 // the (unlikely) chance that we switch to a different mapping function.
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 90426f7..cb65c79 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -75,7 +75,8 @@
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
-    VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
+    VirtualDisplaySurface(HWComposer&, VirtualDisplayIdVariant,
+                          const sp<IGraphicBufferProducer>& sink,
                           const sp<IGraphicBufferProducer>& bqProducer,
                           const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
 
@@ -145,6 +146,7 @@
     void updateQueueBufferOutput(QueueBufferOutput&&);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
+    bool isBackedByGpu() const;
 
     // Both the sink and scratch buffer pools have their own set of slots
     // ("source slots", or "sslot"). We have to merge these into the single
@@ -159,7 +161,7 @@
     // Immutable after construction
     //
     HWComposer& mHwc;
-    const VirtualDisplayId mDisplayId;
+    const VirtualDisplayIdVariant mVirtualIdVariant;
     const std::string mDisplayName;
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 008b057..51d4078 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,7 +29,6 @@
 #include <cinttypes>
 #include <numeric>
 #include <unordered_set>
-#include <vector>
 
 #include "../Jank/JankTracker.h"
 
@@ -1005,11 +1004,6 @@
     finalizeCurrentDisplayFrame();
 }
 
-const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
-        const {
-    return mPresentFrames;
-}
-
 void FrameTimeline::onCommitNotComposited() {
     SFTRACE_CALL();
     std::scoped_lock lock(mMutex);
@@ -1530,7 +1524,6 @@
         mPendingPresentFences.erase(mPendingPresentFences.begin());
     }
 
-    mPresentFrames.clear();
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
         nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1544,12 +1537,6 @@
         auto& displayFrame = pendingPresentFence.second;
         displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
 
-        // Surface frames have been jank classified and can be provided to caller
-        // to detect if buffer stuffing is occurring.
-        for (const auto& frame : displayFrame->getSurfaceFrames()) {
-            mPresentFrames.push_back(frame);
-        }
-
         mPreviousPredictionPresentTime =
                 displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
                                     mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 9fedb57..fa83cd8 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -331,11 +331,6 @@
     virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
                               const std::shared_ptr<FenceTime>& gpuFence) = 0;
 
-    // Provides surface frames that have already been jank classified in the most recent
-    // flush of pending present fences. This allows buffer stuffing detection from SF.
-    virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
-            const = 0;
-
     // Tells FrameTimeline that a frame was committed but not composited. This is used to flush
     // all the associated surface frames.
     virtual void onCommitNotComposited() = 0;
@@ -513,8 +508,6 @@
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
     void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
                       const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
-    const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
-            const override;
     void onCommitNotComposited() override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
@@ -562,9 +555,6 @@
     // display frame, this is a good starting size for the vector so that we can avoid the
     // internal vector resizing that happens with push_back.
     static constexpr uint32_t kNumSurfaceFramesInitial = 10;
-    // Presented surface frames that have been jank classified and can
-    // indicate of potential buffer stuffing.
-    std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 1514340..964a970 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -41,7 +41,7 @@
     if (forceFullDamage) {
         outSurfaceDamageRegion = Region::INVALID_REGION;
     } else {
-        outSurfaceDamageRegion = requested.surfaceDamageRegion;
+        outSurfaceDamageRegion = requested.getSurfaceDamageRegion();
     }
 }
 
@@ -305,7 +305,11 @@
             out << rootId << ",";
         }
     }
-    out << "] " << obj.name << "\n    " << (obj.isVisible ? "visible" : "invisible")
+    out << "] ";
+    if (obj.isSecure) {
+        out << "(Secure) ";
+    }
+    out << obj.name << "\n    " << (obj.isVisible ? "visible" : "invisible")
         << " reason=" << obj.getIsVisibleReason();
 
     if (!obj.geomLayerBounds.isEmpty()) {
@@ -372,7 +376,7 @@
     updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
 
     if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
-        transparentRegionHint = requested.transparentRegion;
+        transparentRegionHint = requested.getTransparentRegion();
     }
     if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
         layerOpaqueFlagSet =
@@ -444,15 +448,7 @@
     }
 
     if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
-        if (requested.windowInfoHandle) {
-            inputInfo = *requested.windowInfoHandle->getInfo();
-        } else {
-            inputInfo = {};
-            // b/271132344 revisit this and see if we can always use the layers uid/pid
-            inputInfo.name = requested.name;
-            inputInfo.ownerUid = requested.ownerUid;
-            inputInfo.ownerPid = requested.ownerPid;
-        }
+        inputInfo = requested.getWindowInfo();
         inputInfo.id = static_cast<int32_t>(uniqueSequence);
         touchCropId = requested.touchCropId;
     }
@@ -544,7 +540,7 @@
         case Composition::INVALID:
             return 'i';
         case Composition::SOLID_COLOR:
-            return 'e';
+            return 'c';
         case Composition::CURSOR:
             return 'u';
         case Composition::SIDEBAND:
@@ -552,7 +548,7 @@
         case Composition::DISPLAY_DECORATION:
             return 'a';
         case Composition::REFRESH_RATE_INDICATOR:
-            return 'f';
+            return 'r';
         case Composition::CLIENT:
         case Composition::DEVICE:
             break;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 86ef6ca..28a6031 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -1090,7 +1090,7 @@
     snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
     const Rect geomLayerBoundsWithoutTransparentRegion =
             RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
-                                        requested.transparentRegion);
+                                        requested.getTransparentRegion());
     snapshot.transformedBoundsWithoutTransparentRegion =
             snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
     snapshot.parentTransform = parentSnapshot.geomLayerTransform;
@@ -1098,7 +1098,7 @@
     if (requested.potentialCursor) {
         // Subtract the transparent region and snap to the bounds
         const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
-                                                        requested.transparentRegion);
+                                                        requested.getTransparentRegion());
         snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
     }
 }
@@ -1132,22 +1132,14 @@
                                        const Args& args) {
     using InputConfig = gui::WindowInfo::InputConfig;
 
-    if (requested.windowInfoHandle) {
-        snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
-    } else {
-        snapshot.inputInfo = {};
-        // b/271132344 revisit this and see if we can always use the layers uid/pid
-        snapshot.inputInfo.name = requested.name;
-        snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid};
-        snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid};
-    }
+    snapshot.inputInfo = requested.getWindowInfo();
     snapshot.touchCropId = requested.touchCropId;
 
     snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
     snapshot.inputInfo.displayId =
             ui::LogicalDisplayId{static_cast<int32_t>(snapshot.outputFilter.layerStack.id)};
     snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
-            ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+            ? requested.getWindowInfo().touchOcclusionMode
             : parentSnapshot.inputInfo.touchOcclusionMode;
     snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation ||
             (requested.flags & layer_state_t::eCanOccludePresentation);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 58c235e..621fd6c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -63,8 +63,11 @@
     metadata.merge(args.metadata);
     changes |= RequestedLayerState::Changes::Metadata;
     handleAlive = true;
-    // TODO: b/305254099 remove once we don't pass invisible windows to input
-    windowInfoHandle = nullptr;
+    // b/271132344 revisit this and see if we can always use the layers uid/pid
+    auto* windowInfo = editWindowInfo();
+    windowInfo->name = name;
+    windowInfo->ownerPid = ownerPid;
+    windowInfo->ownerUid = ownerUid;
     if (parentId != UNASSIGNED_LAYER_ID) {
         canBeRoot = false;
     }
@@ -105,7 +108,7 @@
     currentHdrSdrRatio = 1.f;
     dataspaceRequested = false;
     hdrMetadata.validTypes = 0;
-    surfaceDamageRegion = Region::INVALID_REGION;
+    mNotDefCmpState.surfaceDamageRegion = Region::INVALID_REGION;
     cornerRadius = 0.0f;
     clientDrawnCornerRadius = 0.0f;
     backgroundBlurRadius = 0;
@@ -147,6 +150,7 @@
 }
 
 void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
+    bool transformWasValid = transformIsValid;
     const uint32_t oldFlags = flags;
     const half oldAlpha = color.a;
     const bool hadBuffer = externalTexture != nullptr;
@@ -278,7 +282,7 @@
     if (clientState.what & layer_state_t::eReparent) {
         changes |= RequestedLayerState::Changes::Parent;
         parentId = resolvedComposerState.parentId;
-        parentSurfaceControlForChild = nullptr;
+        mNotDefCmpState.parentSurfaceControlForChild = nullptr;
         // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
         // but thats the existing logic and until we make this behavior more explicit, we need
         // to maintain this logic.
@@ -288,7 +292,7 @@
         changes |= RequestedLayerState::Changes::RelativeParent;
         relativeParentId = resolvedComposerState.relativeParentId;
         isRelativeOf = true;
-        relativeLayerSurfaceControl = nullptr;
+        mNotDefCmpState.relativeLayerSurfaceControl = nullptr;
     }
     if ((clientState.what & layer_state_t::eLayerChanged ||
          (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
@@ -304,7 +308,7 @@
     }
     if (clientState.what & layer_state_t::eInputInfoChanged) {
         touchCropId = resolvedComposerState.touchCropId;
-        windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
+        editWindowInfo()->touchableRegionCropHandle.clear();
     }
     if (clientState.what & layer_state_t::eStretchChanged) {
         stretchEffect.sanitize();
@@ -354,6 +358,14 @@
         clientDrawnCornerRadius = clientState.clientDrawnCornerRadius;
         changes |= RequestedLayerState::Changes::Geometry;
     }
+
+    // We can't just check requestedTransform here because LayerSnapshotBuilder uses
+    // getTransform which reads destinationFrame or buffer dimensions.
+    // Display rotation does not affect validity so just use ROT_0.
+    transformIsValid = LayerSnapshot::isTransformValid(getTransform(ui::Transform::ROT_0));
+    if (!transformWasValid && transformIsValid) {
+        changes |= RequestedLayerState::Changes::Visibility;
+    }
 }
 
 ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -554,12 +566,9 @@
 }
 
 bool RequestedLayerState::hasInputInfo() const {
-    if (!windowInfoHandle) {
-        return false;
-    }
-    const auto windowInfo = windowInfoHandle->getInfo();
-    return windowInfo->token != nullptr ||
-            windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+    const auto& windowInfo = getWindowInfo();
+    return windowInfo.token != nullptr ||
+            windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
 bool RequestedLayerState::needsInputInfo() const {
@@ -571,13 +580,9 @@
         return true;
     }
 
-    if (!windowInfoHandle) {
-        return false;
-    }
-
-    const auto windowInfo = windowInfoHandle->getInfo();
-    return windowInfo->token != nullptr ||
-            windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+    const auto& windowInfo = getWindowInfo();
+    return windowInfo.token != nullptr ||
+            windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
 bool RequestedLayerState::hasBufferOrSidebandStream() const {
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 7232379..b8310be 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -115,6 +115,7 @@
     const gui::Pid ownerPid;
     bool dataspaceRequested;
     bool hasColorTransform;
+    bool transformIsValid = true;
     bool premultipliedAlpha{true};
     // This layer can be a cursor on some displays.
     bool potentialCursor{false};
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 614f33f..758b111 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -19,11 +19,11 @@
 #include <android-base/thread_annotations.h>
 #include <android/gui/IHdrLayerInfoListener.h>
 #include <binder/IBinder.h>
+#include <ui/RingBuffer.h>
 #include <utils/Timers.h>
 
 #include <unordered_map>
 
-#include "Utils/RingBuffer.h"
 #include "WpHash.h"
 
 namespace android {
@@ -102,7 +102,7 @@
         EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); }
     };
 
-    utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
+    ui::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
 };
 
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e1bba44..2e31282 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -362,7 +362,7 @@
 // transaction
 // ----------------------------------------------------------------------------
 
-void Layer::commitTransaction() {
+void Layer::commitTransaction() REQUIRES(mFlinger->mStateLock) {
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
     // bufferSurfaceFrameTX will be presented in latchBuffer.
     for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -394,7 +394,8 @@
 };
 
 void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
-                                                      nsecs_t postTime, gui::GameMode gameMode) {
+                                                      nsecs_t postTime, gui::GameMode gameMode)
+        REQUIRES(mFlinger->mStateLock) {
     mDrawingState.postTime = postTime;
 
     // Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
@@ -458,7 +459,7 @@
 
 void Layer::addSurfaceFramePresentedForBuffer(
         std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
-        nsecs_t currentLatchTime) {
+        nsecs_t currentLatchTime) REQUIRES(mFlinger->mStateLock) {
     surfaceFrame->setAcquireFenceTime(acquireFenceTime);
     surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
     mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -466,7 +467,8 @@
 }
 
 std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
-        const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) {
+        const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode)
+        REQUIRES(mFlinger->mStateLock) {
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName,
@@ -488,7 +490,7 @@
 
 std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
         const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
-        gui::GameMode gameMode) {
+        gui::GameMode gameMode) REQUIRES(mFlinger->mStateLock) {
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
@@ -506,7 +508,8 @@
 }
 
 void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
-                                                  std::string debugName, gui::GameMode gameMode) {
+                                                  std::string debugName, gui::GameMode gameMode)
+        REQUIRES(mFlinger->mStateLock) {
     if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
         return;
     }
@@ -719,6 +722,10 @@
     uint32_t currentMaxAcquiredBufferCount =
             mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
 
+    if (FlagManager::getInstance().monitor_buffer_fences()) {
+        buffer->getDependencyMonitor().addEgress(FenceTime::makeValid(fence), "Layer release");
+    }
+
     if (listener) {
         listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
     }
@@ -842,7 +849,7 @@
     return true;
 }
 
-void Layer::releasePreviousBuffer() {
+void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) {
     mReleasePreviousBuffer = true;
     if (!mBufferInfo.mBuffer ||
         (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
@@ -884,7 +891,8 @@
 
 bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
                       const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
-                      bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) {
+                      bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode)
+        REQUIRES(mFlinger->mStateLock) {
     SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
 
     const bool frameNumberChanged =
@@ -936,6 +944,7 @@
             std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
 
     mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+    mDrawingState.previousBuffer = std::move(mDrawingState.buffer);
     mDrawingState.buffer = std::move(buffer);
     mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
             ? bufferData.acquireFence
@@ -1074,7 +1083,8 @@
 }
 
 bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
-                              nsecs_t postTime, gui::GameMode gameMode) {
+                              nsecs_t postTime, gui::GameMode gameMode)
+        REQUIRES(mFlinger->mStateLock) {
     if (mDrawingState.sidebandStream == sidebandStream) return false;
 
     if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -1117,6 +1127,7 @@
             handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
             handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+            handle->previousBuffer = mDrawingState.previousBuffer;
             if (mPreviousReleaseBufferEndpoint == handle->listener) {
                 // Add fence from previous screenshot now so that it can be dispatched to the
                 // client.
@@ -1207,7 +1218,7 @@
     return false;
 }
 
-void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) REQUIRES(mFlinger->mStateLock) {
     const State& s(getDrawingState());
 
     if (!s.buffer) {
@@ -1428,8 +1439,8 @@
                                                presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
             mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-        } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-                   displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+        } else if (const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
+                   displayId.has_value() && mFlinger->getHwComposer().isConnected(*displayId)) {
             // The HWC doesn't support present fences, so use the present timestamp instead.
             const nsecs_t presentTimestamp =
                     mFlinger->getHwComposer().getPresentTimestamp(*displayId);
@@ -1457,7 +1468,8 @@
     mBufferInfo.mFrameLatencyNeeded = false;
 }
 
-bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly)
+        REQUIRES(mFlinger->mStateLock) {
     SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
                            getDrawingState().frameNumber);
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 6af0f59..88754f9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -117,6 +117,7 @@
         uint32_t bufferTransform;
         bool transformToDisplayInverse;
         Region transparentRegionHint;
+        std::shared_ptr<renderengine::ExternalTexture> previousBuffer;
         std::shared_ptr<renderengine::ExternalTexture> buffer;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
@@ -288,7 +289,7 @@
                                         bool leaveState);
 
     inline bool hasTrustedPresentationListener() {
-        return mTrustedPresentationListener.callbackInterface != nullptr;
+        return mTrustedPresentationListener.getCallback() != nullptr;
     }
 
     // Sets the masked bits.
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b619268..5e076bd 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -410,6 +410,15 @@
     if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) {
         return;
     }
+
+    if (releaseFence.has_value()) {
+        if (FlagManager::getInstance().monitor_buffer_fences()) {
+            if (auto strongBuffer = mReleasedBuffer.promote()) {
+                strongBuffer->getDependencyMonitor()
+                        .addAccessCompletion(FenceTime::makeValid(releaseFence.value()), "HWC");
+            }
+        }
+    }
     mReleaseFence.set_value(releaseFence);
     mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED;
 }
@@ -428,6 +437,10 @@
     return mReleaseFencePromiseStatus;
 }
 
+void LayerFE::setReleasedBuffer(sp<GraphicBuffer> buffer) {
+    mReleasedBuffer = std::move(buffer);
+}
+
 void LayerFE::setLastHwcState(const LayerFE::HwcLayerDebugState &state) {
     mLastHwcState = state;
 }
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index a537456..b89b6b4 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,6 +18,7 @@
 
 #include <android/gui/CachingHint.h>
 #include <gui/LayerMetadata.h>
+#include <ui/GraphicBuffer.h>
 #include <ui/LayerStack.h>
 #include <ui/PictureProfileHandle.h>
 
@@ -58,6 +59,7 @@
     ftl::Future<FenceResult> createReleaseFenceFuture() override;
     void setReleaseFence(const FenceResult& releaseFence) override;
     LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+    void setReleasedBuffer(sp<GraphicBuffer> buffer) override;
     void onPictureProfileCommitted() override;
 
     // Used for debugging purposes, e.g. perfetto tracing, dumpsys.
@@ -95,6 +97,7 @@
     std::promise<FenceResult> mReleaseFence;
     ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED;
     HwcLayerDebugState mLastHwcState;
+    wp<GraphicBuffer> mReleasedBuffer;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 44cd319..84b1a73 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -447,7 +447,7 @@
     }
     layerInfo->set_type("Layer");
 
-    LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+    LayerProtoHelper::writeToProto(requestedState.getTransparentRegion(),
                                    [&]() { return layerInfo->mutable_transparent_region(); });
 
     layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 38dc11d..81155fd 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -49,7 +49,8 @@
     using Visitor = std::function<void(Layer*)>;
 
 private:
-    const StateSet mStateSet;
+    // FIXME: This is set but not used anywhere.
+    [[maybe_unused]] const StateSet mStateSet;
 };
 }
 
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index cd7210c..788448d 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -515,7 +515,7 @@
 }
 
 void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
-    mExpectedPresentTimes.append(expectedPresentTime);
+    mExpectedPresentTimes.next() = expectedPresentTime;
 }
 
 void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
@@ -532,7 +532,7 @@
 }
 
 void PowerAdvisor::setCommitStart(TimePoint commitStartTime) {
-    mCommitStartTimes.append(commitStartTime);
+    mCommitStartTimes.next() = commitStartTime;
 }
 
 void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) {
@@ -579,7 +579,7 @@
     }
 
     // Tracks when we finish presenting to hwc
-    TimePoint estimatedHwcEndTime = mCommitStartTimes[0];
+    TimePoint estimatedHwcEndTime = mCommitStartTimes.back();
 
     // How long we spent this frame not doing anything, waiting for fences or vsync
     Duration idleDuration = 0ns;
@@ -643,13 +643,13 @@
     // Also add the frame delay duration since the target did not move while we were delayed
     Duration totalDuration = mFrameDelayDuration +
             std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
-            mCommitStartTimes[0];
+            mCommitStartTimes.back();
     Duration totalDurationWithoutGpu =
-            mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0];
+            mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes.back();
 
     // We finish SurfaceFlinger when post-composition finishes, so add that in here
     Duration flingerDuration =
-            estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+            estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes.back();
     Duration estimatedGpuDuration = firstGpuTimeline.has_value()
             ? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime
             : Duration::fromNs(0);
@@ -661,7 +661,7 @@
     hal::WorkDuration duration{
             .timeStampNanos = TimePoint::now().ns(),
             .durationNanos = combinedDuration.ns(),
-            .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+            .workPeriodStartTimestampNanos = mCommitStartTimes.back().ns(),
             .cpuDurationNanos = supportsGpuReporting() ? cpuDuration.ns() : 0,
             .gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0,
     };
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 540a9df..b97160a 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -23,6 +23,7 @@
 
 #include <ui/DisplayId.h>
 #include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
 #include <utils/Mutex.h>
 
 // FMQ library in IPower does questionable conversions
@@ -247,27 +248,6 @@
         std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime);
     };
 
-    template <class T, size_t N>
-    class RingBuffer {
-        std::array<T, N> elements = {};
-        size_t mIndex = 0;
-        size_t numElements = 0;
-
-    public:
-        void append(T item) {
-            mIndex = (mIndex + 1) % N;
-            numElements = std::min(N, numElements + 1);
-            elements[mIndex] = item;
-        }
-        bool isFull() const { return numElements == N; }
-        // Allows access like [0] == current, [-1] = previous, etc..
-        T& operator[](int offset) {
-            size_t positiveOffset =
-                    static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
-            return elements[(mIndex + positiveOffset) % N];
-        }
-    };
-
     // Filter and sort the display ids by a given property
     std::vector<DisplayId> getOrderedDisplayIds(
             std::optional<TimePoint> DisplayTimingData::*sortBy);
@@ -287,9 +267,9 @@
     // Last frame's post-composition duration
     Duration mLastPostcompDuration{0ns};
     // Buffer of recent commit start times
-    RingBuffer<TimePoint, 2> mCommitStartTimes;
+    ui::RingBuffer<TimePoint, 2> mCommitStartTimes;
     // Buffer of recent expected present times
-    RingBuffer<TimePoint, 2> mExpectedPresentTimes;
+    ui::RingBuffer<TimePoint, 2> mExpectedPresentTimes;
     // Most recent present fence time, provided by SF after composition engine finishes presenting
     TimePoint mLastPresentFenceTime;
     // Most recent composition engine present end time, returned with the present fence from SF
diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.h b/services/surfaceflinger/PowerAdvisor/SessionManager.h
index 93a80b5..afa52eb 100644
--- a/services/surfaceflinger/PowerAdvisor/SessionManager.h
+++ b/services/surfaceflinger/PowerAdvisor/SessionManager.h
@@ -68,7 +68,8 @@
     bool isLayerRelevant(int32_t layerId);
 
     // The UID of whoever created our ISessionManager connection
-    const uid_t mUid;
+    // FIXME: This is set but is not used anywhere.
+    [[maybe_unused]] const uid_t mUid;
 
     // State owned by the main thread
 
@@ -99,4 +100,4 @@
 };
 
 } // namespace adpf
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 514adac..615492a 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -348,7 +348,7 @@
 
     SurfaceFlinger::ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = std::nullopt;
+    screenshotArgs.displayIdVariant = std::nullopt;
     screenshotArgs.sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds;
     screenshotArgs.reqSize = sampledBounds.getSize();
     screenshotArgs.dataspace = ui::Dataspace::V0_SRGB;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index c37b965..5390295 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -512,14 +512,6 @@
     mCondition.notify_all();
 }
 
-// Merge lists of buffer stuffed Uids
-void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    for (auto& [uid, count] : bufferStuffedUids) {
-        mBufferStuffedUids.emplace_or_replace(uid, count);
-    }
-}
-
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
@@ -761,10 +753,6 @@
 
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
-    // List of Uids that have been sent vsync data with queued buffer count.
-    // Used to keep track of which Uids can be removed from the map of
-    // buffer stuffed clients.
-    ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
     for (const auto& consumer : consumers) {
         DisplayEventReceiver::Event copy = event;
         if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) {
@@ -774,13 +762,6 @@
                                   event.vsync.vsyncData.preferredExpectedPresentationTime(),
                                   event.vsync.vsyncData.preferredDeadlineTimestamp());
         }
-        auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
-        if (it != mBufferStuffedUids.end()) {
-            copy.vsync.vsyncData.numberQueuedBuffers = it->second;
-            uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
-        } else {
-            copy.vsync.vsyncData.numberQueuedBuffers = 0;
-        }
         switch (consumer->postEvent(copy)) {
             case NO_ERROR:
                 break;
@@ -796,12 +777,6 @@
                 removeDisplayEventConnectionLocked(consumer);
         }
     }
-    // The clients that have already received the queued buffer count
-    // can be removed from the buffer stuffed Uid list to avoid
-    // being sent duplicate messages.
-    for (auto uid : uidsPostedQueuedBuffers) {
-        mBufferStuffedUids.erase(uid);
-    }
     if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC &&
         FlagManager::getInstance().vrr_config()) {
         mLastCommittedVsyncTime =
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 18bf416..612883a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -56,7 +56,6 @@
 // ---------------------------------------------------------------------------
 
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
 
 enum class VSyncRequest {
     None = -2,
@@ -141,10 +140,6 @@
 
     virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
                                      int32_t maxLevel) = 0;
-
-    // An elevated number of queued buffers in the server is detected. This propagates a
-    // flag to Choreographer indicating that buffer stuffing recovery should begin.
-    virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
 };
 
 struct IEventThreadCallback {
@@ -199,8 +194,6 @@
     void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
                              int32_t maxLevel) override;
 
-    void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
-
 private:
     friend EventThreadTest;
 
@@ -241,10 +234,6 @@
     scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
-    // All consumers that need to recover from buffer stuffing and the number
-    // of their queued buffers.
-    BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
-
     IEventThreadCallback& mCallback;
 
     std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4da76f6..c9d3b31 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -554,8 +554,7 @@
     ftl::FakeGuard guard(kMainThreadContext);
 
     for (const auto& [id, display] : mDisplays) {
-        if (display.powerMode != hal::PowerMode::OFF ||
-            !FlagManager::getInstance().multithreaded_present()) {
+        if (display.powerMode != hal::PowerMode::OFF) {
             resyncToHardwareVsyncLocked(id, allowToEnable);
         }
     }
@@ -961,11 +960,6 @@
     return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
 }
 
-void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
-    if (!mRenderEventThread) return;
-    mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
-}
-
 void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
@@ -986,7 +980,7 @@
     if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
         const Display& pacesetter = *pacesetterOpt;
 
-        if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+        if (params.toggleIdleTimer) {
             pacesetter.selectorPtr->setIdleTimerCallbacks(
                     {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
                                   .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
@@ -1018,7 +1012,7 @@
 }
 
 void Scheduler::demotePacesetterDisplay(PromotionParams params) {
-    if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+    if (params.toggleIdleTimer) {
         // No need to lock for reads on kMainThreadContext.
         if (const auto pacesetterPtr =
                     FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3fdddac..61469c1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -337,10 +337,6 @@
         mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
     }
 
-    // Propagates a flag to the EventThread indicating that buffer stuffing
-    // recovery should begin.
-    void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
-
     void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; }
 
 private:
@@ -386,7 +382,7 @@
     // a deadlock where the main thread joins with the timer thread as the timer thread waits to
     // lock a mutex held by the main thread.
     struct PromotionParams {
-        // Whether to stop and start the idle timer. Ignored unless connected_display flag is set.
+        // Whether to stop and start the idle timer.
         bool toggleIdleTimer;
     };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ff360b7..bb04d12 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -206,7 +206,12 @@
     // Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
     const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
     auto it = mRateMap.find(idealPeriod());
-    auto const currentPeriod = it->second.slope;
+    // Calculated slope over the period of time can become outdated as the new timestamps are
+    // stored. Using idealPeriod instead provides a rate which is valid at all the times.
+    auto const currentPeriod =
+            mDisplayModePtr->getVrrConfig() && FlagManager::getInstance().vsync_predictor_recovery()
+            ? idealPeriod()
+            : it->second.slope;
 
     // The mean of the ordinals must be precise for the intercept calculation, so scale them up for
     // fixed-point arithmetic.
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 813d4de..ff461d2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -24,6 +24,7 @@
 #include <ui/DisplayId.h>
 #include <ui/Fence.h>
 #include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
 
 #include <scheduler/Features.h>
 #include <scheduler/FrameTime.h>
@@ -34,7 +35,6 @@
 // TODO(b/185536303): Pull to FTL.
 #include "../../../TracedOrdinal.h"
 #include "../../../Utils/Dumper.h"
-#include "../../../Utils/RingBuffer.h"
 
 namespace android::scheduler {
 
@@ -108,7 +108,7 @@
     std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
             Period vsyncPeriod, Period minFramePeriod) const;
     std::array<PresentFence, 2> mPresentFencesLegacy;
-    utils::RingBuffer<PresentFence, 5> mPresentFences;
+    ui::RingBuffer<PresentFence, 5> mPresentFences;
 
     FrameTime mLastSignaledFrameTime;
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 767462d..70ae940 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -36,7 +36,7 @@
 
 using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
 
-using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;
+using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayIdVariant, CompositionCoverageFlags>;
 
 inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
     CompositionCoverageFlags coverage;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 7123905..2906bbd 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -30,11 +30,12 @@
 std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
     std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
             ScreenCaptureOutput, compositionengine::CompositionEngine,
-            /* sourceCrop */ const Rect, std::optional<DisplayId>,
+            /* sourceCrop */ const Rect, ftl::Optional<DisplayIdVariant>,
             const compositionengine::Output::ColorProfile&,
             /* layerAlpha */ float,
-            /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop, args.displayId,
-                                       args.colorProfile, args.layerAlpha, args.regionSampling,
+            /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop,
+                                       args.displayIdVariant, args.colorProfile, args.layerAlpha,
+                                       args.regionSampling,
                                        args.dimInGammaSpaceForEnhancedScreenshots,
                                        args.enableLocalTonemapping);
     output->editState().isSecure = args.isSecure;
@@ -59,8 +60,8 @@
 
     {
         std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
-        if (args.displayId) {
-            base::StringAppendF(&name, " for %" PRIu64, args.displayId.value().value);
+        if (const auto id = args.displayIdVariant.and_then(asDisplayIdOfType<DisplayId>)) {
+            base::StringAppendF(&name, " for %" PRIu64, id->value);
         }
         output->setName(name);
     }
@@ -68,12 +69,12 @@
 }
 
 ScreenCaptureOutput::ScreenCaptureOutput(
-        const Rect sourceCrop, std::optional<DisplayId> displayId,
+        const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
         const compositionengine::Output::ColorProfile& colorProfile, float layerAlpha,
         bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
         bool enableLocalTonemapping)
       : mSourceCrop(sourceCrop),
-        mDisplayId(displayId),
+        mDisplayIdVariant(displayIdVariant),
         mColorProfile(colorProfile),
         mLayerAlpha(layerAlpha),
         mRegionSampling(regionSampling),
@@ -137,12 +138,9 @@
         }
 
         std::vector<aidl::android::hardware::graphics::composer3::Luts> luts;
-        if (mDisplayId) {
-            const auto id = PhysicalDisplayId::tryCast(mDisplayId.value());
-            if (id) {
-                auto& hwc = getCompositionEngine().getHwComposer();
-                hwc.getLuts(*id, buffers, &luts);
-            }
+        if (const auto physicalDisplayId = mDisplayIdVariant.and_then(asPhysicalDisplayId)) {
+            auto& hwc = getCompositionEngine().getHwComposer();
+            hwc.getLuts(*physicalDisplayId, buffers, &luts);
         }
 
         if (buffers.size() == luts.size()) {
@@ -209,6 +207,7 @@
     }
 
     compositionengine::LayerFE::LayerSettings fillLayer;
+    fillLayer.name = "ScreenCaptureFillLayer";
     fillLayer.source.buffer.buffer = nullptr;
     fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f);
     fillLayer.geometry.boundaries =
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index b3e98b1..d4e20fc 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -30,7 +30,7 @@
     ui::LayerStack layerStack;
     Rect sourceCrop;
     std::shared_ptr<renderengine::ExternalTexture> buffer;
-    std::optional<DisplayId> displayId;
+    ftl::Optional<DisplayIdVariant> displayIdVariant;
     ui::Size reqBufferSize;
     float sdrWhitePointNits;
     float displayBrightnessNits;
@@ -51,7 +51,7 @@
 // SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay.
 class ScreenCaptureOutput : public compositionengine::impl::Output {
 public:
-    ScreenCaptureOutput(const Rect sourceCrop, std::optional<DisplayId> displayId,
+    ScreenCaptureOutput(const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
                         const compositionengine::Output::ColorProfile& colorProfile,
                         float layerAlpha, bool regionSampling,
                         bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping);
@@ -70,7 +70,7 @@
 private:
     std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts();
     const Rect mSourceCrop;
-    const std::optional<DisplayId> mDisplayId;
+    const ftl::Optional<DisplayIdVariant> mDisplayIdVariant;
     const compositionengine::Output::ColorProfile& mColorProfile;
     const float mLayerAlpha;
     const bool mRegionSampling;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f3db4c5..ce7a720 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -66,13 +66,11 @@
 #include <ftl/concat.h>
 #include <ftl/fake_guard.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
 #include <ftl/unit.h>
 #include <gui/AidlUtil.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/IProducerListener.h>
-#include <gui/JankInfo.h>
 #include <gui/LayerMetadata.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
@@ -384,6 +382,7 @@
 bool SurfaceFlinger::hasSyncFramework;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 int64_t SurfaceFlinger::minAcquiredBuffers = 1;
+std::optional<int64_t> SurfaceFlinger::maxAcquiredBuffersOpt;
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::useContextPriority;
@@ -452,6 +451,7 @@
     maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
     minAcquiredBuffers =
             SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
+    maxAcquiredBuffersOpt = SurfaceFlingerProperties::max_acquired_buffers();
 
     maxGraphicsWidth = std::max(max_graphics_width(0), 0);
     maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -576,9 +576,10 @@
     mScheduler->run();
 }
 
-sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure,
-                                                 const std::string& uniqueId,
-                                                 float requestedRefreshRate) {
+sp<IBinder> SurfaceFlinger::createVirtualDisplay(
+        const std::string& displayName, bool isSecure,
+        gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+        float requestedRefreshRate) {
     // SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
     // This is to ensure that only root, system, and graphics can request to create a secure
     // display. Secure displays can show secure content so we add an additional restriction on it.
@@ -588,6 +589,8 @@
         return nullptr;
     }
 
+    ALOGD("Creating virtual display: %s", displayName.c_str());
+
     class DisplayToken : public BBinder {
         sp<SurfaceFlinger> flinger;
         virtual ~DisplayToken() {
@@ -611,6 +614,9 @@
     // Set display as protected when marked as secure to ensure no behavior change
     // TODO (b/314820005): separate as a different arg when creating the display.
     state.isProtected = isSecure;
+    state.optimizationPolicy = optimizationPolicy;
+    // Virtual displays start in ON mode.
+    state.initialPowerMode = hal::PowerMode::ON;
     state.displayName = displayName;
     state.uniqueId = uniqueId;
     state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
@@ -632,6 +638,9 @@
         ALOGE("%s: Invalid operation on physical display", __func__);
         return INVALID_OPERATION;
     }
+
+    ALOGD("Destroying virtual display: %s", state.displayName.c_str());
+
     mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
     return NO_ERROR;
@@ -648,12 +657,14 @@
     }
 }
 
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
-                                                       const std::string& uniqueId) {
+std::optional<VirtualDisplayIdVariant> SurfaceFlinger::acquireVirtualDisplay(
+        ui::Size resolution, ui::PixelFormat format, const std::string& uniqueId,
+        compositionengine::DisplayCreationArgsBuilder& builder) {
     if (auto& generator = mVirtualDisplayIdGenerators.hal) {
         if (const auto id = generator->generateId()) {
             if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
                 acquireVirtualDisplaySnapshot(*id, uniqueId);
+                builder.setId(*id);
                 return *id;
             }
 
@@ -668,22 +679,23 @@
     const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
     LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
     acquireVirtualDisplaySnapshot(*id, uniqueId);
+    builder.setId(*id);
     return *id;
 }
 
-void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) {
-    if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
-        if (auto& generator = mVirtualDisplayIdGenerators.hal) {
-            generator->releaseId(*id);
-            releaseVirtualDisplaySnapshot(*id);
-        }
-        return;
-    }
-
-    const auto id = GpuVirtualDisplayId::tryCast(displayId);
-    LOG_ALWAYS_FATAL_IF(!id);
-    mVirtualDisplayIdGenerators.gpu.releaseId(*id);
-    releaseVirtualDisplaySnapshot(*id);
+void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayIdVariant displayId) {
+    ftl::match(
+            displayId,
+            [this](HalVirtualDisplayId halVirtualDisplayId) {
+                if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+                    generator->releaseId(halVirtualDisplayId);
+                    releaseVirtualDisplaySnapshot(halVirtualDisplayId);
+                }
+            },
+            [this](GpuVirtualDisplayId gpuVirtualDisplayId) {
+                mVirtualDisplayIdGenerators.gpu.releaseId(gpuVirtualDisplayId);
+                releaseVirtualDisplaySnapshot(gpuVirtualDisplayId);
+            });
 }
 
 void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
@@ -1009,9 +1021,8 @@
     mPowerAdvisor->init();
 
     if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) {
-        if (setSchedFifo(false) != NO_ERROR) {
-            ALOGW("Can't set SCHED_OTHER for primeCache");
-        }
+        constexpr const char* kWhence = "primeCache";
+        setSchedFifo(false, kWhence);
 
         mRenderEnginePrimeCacheFuture.callOnce([this] {
             renderengine::PrimeCacheConfig config;
@@ -1047,9 +1058,7 @@
             return getRenderEngine().primeCache(config);
         });
 
-        if (setSchedFifo(true) != NO_ERROR) {
-            ALOGW("Can't set SCHED_FIFO after primeCache");
-        }
+        setSchedFifo(true, kWhence);
     }
 
     // Avoid blocking the main thread on `init` to set properties.
@@ -1174,6 +1183,7 @@
     const auto& snapshot = snapshotRef.get();
 
     info->connectionType = snapshot.connectionType();
+    info->port = snapshot.port();
     info->deviceProductInfo = snapshot.deviceProductInfo();
 
     if (mEmulatedDisplayDensity) {
@@ -1257,7 +1267,17 @@
     ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
     info->frameRateCategoryRate = frameRateCategoryRate;
 
-    info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+    if (info->hasArrSupport) {
+        info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+    } else {
+        // On non-ARR devices, list the refresh rates same as the supported display modes.
+        std::vector<float> supportedFrameRates;
+        supportedFrameRates.reserve(info->supportedDisplayModes.size());
+        std::transform(info->supportedDisplayModes.begin(), info->supportedDisplayModes.end(),
+                       std::back_inserter(supportedFrameRates),
+                       [](ui::DisplayMode mode) { return mode.peakRefreshRate; });
+        info->supportedRefreshRates = supportedFrameRates;
+    }
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
@@ -2286,8 +2306,7 @@
 
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
-    if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
-        vsyncPeriod.has_value()) {
+    if (timestamp < 0 && vsyncPeriod.has_value()) {
         if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
             const int32_t value = static_cast<int32_t>(-timestamp);
             // one byte is good enough to encode android.hardware.drm.HdcpLevel
@@ -2339,9 +2358,19 @@
         return;
     }
 
-    if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
-        !FlagManager::getInstance().display_config_error_hal()) {
-        return;
+    if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+        if (!FlagManager::getInstance().display_config_error_hal()) {
+            return;
+        }
+        {
+            std::lock_guard<std::mutex> lock(mHotplugMutex);
+            mPendingHotplugEvents.push_back(
+                    HotplugEvent{hwcDisplayId, HWComposer::HotplugEvent::LinkUnstable});
+        }
+        if (mScheduler) {
+            mScheduler->scheduleConfigure();
+        }
+        // do not return to also report the error.
     }
 
     // TODO(b/311403559): use enum type instead of int
@@ -2483,7 +2512,8 @@
 }
 
 bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
-                                          bool flushTransactions, bool& outTransactionsAreEmpty) {
+                                          bool flushTransactions, bool& outTransactionsAreEmpty)
+        EXCLUDES(mStateLock) {
     using Changes = frontend::RequestedLayerState::Changes;
     SFTRACE_CALL();
     SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Transaction Handling");
@@ -2680,7 +2710,7 @@
 }
 
 bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
-                            const scheduler::FrameTargets& frameTargets) {
+                            const scheduler::FrameTargets& frameTargets) EXCLUDES(mStateLock) {
     const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
 
     const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
@@ -2845,9 +2875,9 @@
     // output. Layer stacks are not tracked in Display when we iterate through
     // frameTargeters. Cross-referencing layer stacks allows us to filter out displays
     // by ID with duplicate layer stacks before adding them to CompositionEngine output.
-    ui::DisplayMap<DisplayId, ui::LayerStack> physicalDisplayLayerStacks;
+    ui::DisplayMap<PhysicalDisplayId, ui::LayerStack> physicalDisplayLayerStacks;
     for (auto& [_, display] : displays) {
-        const auto id = PhysicalDisplayId::tryCast(display->getId());
+        const auto id = asPhysicalDisplayId(display->getDisplayIdVariant());
         if (id && frameTargeters.contains(*id)) {
             physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack());
         }
@@ -2855,18 +2885,20 @@
 
     // Tracks layer stacks of displays that are added to CompositionEngine output.
     ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks;
-    auto isOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
-        if (FlagManager::getInstance().reject_dupe_layerstacks() &&
-            outputLayerStacks.contains(layerStack)) {
-            // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
-            // removed
-            ALOGD("Existing layer stack ID %d output to another display %" PRIu64
-                  ", dropping display from outputs",
-                  layerStack.id, id.value);
-            return true;
+    auto isUniqueOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
+        if (FlagManager::getInstance().reject_dupe_layerstacks()) {
+            if (layerStack != ui::INVALID_LAYER_STACK && outputLayerStacks.contains(layerStack)) {
+                // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
+                // removed
+                ALOGD("Existing layer stack ID %d output to another display %" PRIu64
+                      ", dropping display from outputs",
+                      layerStack.id, id.value);
+                return false;
+            }
         }
+
         outputLayerStacks.try_emplace(layerStack);
-        return false;
+        return true;
     };
 
     // Add outputs for physical displays.
@@ -2875,7 +2907,7 @@
 
         if (const auto display = getCompositionDisplayLocked(id)) {
             const auto layerStack = physicalDisplayLayerStacks.get(id)->get();
-            if (!isOutputLayerStack(display->getId(), layerStack)) {
+            if (isUniqueOutputLayerStack(display->getId(), layerStack)) {
                 refreshArgs.outputs.push_back(display);
             }
         }
@@ -2894,7 +2926,7 @@
 
             if (!refreshRate.isValid() ||
                 mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
-                if (!isOutputLayerStack(display->getId(), display->getLayerStack())) {
+                if (isUniqueOutputLayerStack(display->getId(), display->getLayerStack())) {
                     refreshArgs.outputs.push_back(display->getCompositionDisplay());
                 }
             }
@@ -3111,7 +3143,7 @@
     for (const auto& [_, display] : displays) {
         const auto& state = display->getCompositionDisplay()->getState();
         CompositionCoverageFlags& flags =
-                mCompositionCoverage.try_emplace(display->getId()).first->second;
+                mCompositionCoverage.try_emplace(display->getDisplayIdVariant()).first->second;
 
         if (state.usesDeviceComposition) {
             flags |= CompositionCoverage::Hwc;
@@ -3165,8 +3197,8 @@
     CompositeResultsPerDisplay resultsPerDisplay;
 
     // Filter out virtual displays.
-    for (const auto& [id, coverage] : mCompositionCoverage) {
-        if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+    for (const auto& [idVar, coverage] : mCompositionCoverage) {
+        if (const auto idOpt = asPhysicalDisplayId(idVar)) {
             resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
         }
     }
@@ -3204,16 +3236,12 @@
     return false;
 }
 
-ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
+ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(PhysicalDisplayId displayId,
                                                            bool isPrimary) const {
-    const auto id = PhysicalDisplayId::tryCast(displayId);
-    if (!id) {
-        return ui::ROTATION_0;
-    }
     if (!mIgnoreHwcPhysicalDisplayOrientation &&
         getHwComposer().getComposer()->isSupported(
                 Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
-        switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
+        switch (getHwComposer().getPhysicalDisplayOrientation(displayId)) {
             case Hwc2::AidlTransform::ROT_90:
                 return ui::ROTATION_90;
             case Hwc2::AidlTransform::ROT_180:
@@ -3285,40 +3313,12 @@
 
     const TimePoint presentTime = TimePoint::now();
 
-    // The Uids of layer owners that are in buffer stuffing mode, and their elevated
-    // buffer counts. Messages to start recovery are sent exclusively to these Uids.
-    BufferStuffingMap bufferStuffedUids;
-
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
     mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
                                  pacesetterGpuCompositionDoneFenceTime);
 
-    // Find and register any layers that are in buffer stuffing mode
-    const auto& presentFrames = mFrameTimeline->getPresentFrames();
-
-    for (const auto& frame : presentFrames) {
-        const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId());
-        if (!layer) continue;
-        uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0;
-        int32_t jankType = frame->getJankType().value_or(JankType::None);
-        if (jankType & JankType::BufferStuffing &&
-            layer->flags & layer_state_t::eRecoverableFromBufferStuffing) {
-            auto [it, wasEmplaced] =
-                    bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers);
-            // Update with maximum number of queued buffers, allows clients drawing
-            // multiple windows to account for the most severely stuffed window
-            if (!wasEmplaced && it->second < numberQueuedBuffers) {
-                it->second = numberQueuedBuffers;
-            }
-        }
-    }
-
-    if (!bufferStuffedUids.empty()) {
-        mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids));
-    }
-
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
     // but that should be okay since CompositorTiming has snapping logic.
@@ -3551,9 +3551,8 @@
     std::vector<HWComposer::HWCDisplayMode> hwcModes;
     std::optional<hal::HWConfigId> activeModeHwcIdOpt;
 
-    const bool isExternalDisplay = FlagManager::getInstance().connected_display() &&
-            getHwComposer().getDisplayConnectionType(displayId) ==
-                    ui::DisplayConnectionType::External;
+    const bool isExternalDisplay = getHwComposer().getDisplayConnectionType(displayId) ==
+            ui::DisplayConnectionType::External;
 
     int attempt = 0;
     constexpr int kMaxAttempts = 3;
@@ -3716,11 +3715,12 @@
             const auto displayId = info->id;
             const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId,
                                             ')');
-
-            if (event == HWComposer::HotplugEvent::Connected) {
+            // TODO: b/393126541 - replace if with switch as all cases are handled.
+            if (event == HWComposer::HotplugEvent::Connected ||
+                event == HWComposer::HotplugEvent::LinkUnstable) {
                 const auto activeModeIdOpt =
                         processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
-                                              displayString.c_str());
+                                              displayString.c_str(), event);
                 if (!activeModeIdOpt) {
                     mScheduler->dispatchHotplugError(
                             static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
@@ -3746,7 +3746,7 @@
                 LOG_ALWAYS_FATAL_IF(!snapshotOpt);
 
                 mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config);
-            } else {
+            } else { // event == HWComposer::HotplugEvent::Disconnected
                 // Unregister before destroying the DisplaySnapshot below.
                 mDisplayModeController.unregisterDisplay(displayId);
 
@@ -3761,7 +3761,8 @@
 std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
                                                                    hal::HWDisplayId hwcDisplayId,
                                                                    DisplayIdentificationInfo&& info,
-                                                                   const char* displayString) {
+                                                                   const char* displayString,
+                                                                   HWComposer::HotplugEvent event) {
     auto [displayModes, activeMode] = loadDisplayModes(displayId);
     if (!activeMode) {
         ALOGE("Failed to hotplug %s", displayString);
@@ -3796,6 +3797,9 @@
         state.physical->port = port;
         ALOGI("Reconnecting %s", displayString);
         return activeModeId;
+    } else if (event == HWComposer::HotplugEvent::LinkUnstable) {
+        ALOGE("Failed to reconnect unknown %s", displayString);
+        return std::nullopt;
     }
 
     const sp<IBinder> token = sp<BBinder>::make();
@@ -3857,13 +3861,17 @@
     creationArgs.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
 
-    if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+    if (const auto physicalIdOpt =
+                compositionDisplay->getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         const auto physicalId = *physicalIdOpt;
 
         creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked();
         creationArgs.refreshRateSelector =
                 FTL_FAKE_GUARD(kMainThreadContext,
                                mDisplayModeController.selectorPtrFor(physicalId));
+        creationArgs.physicalOrientation =
+                getPhysicalDisplayOrientation(physicalId, creationArgs.isPrimary);
+        ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
         mPhysicalDisplays.get(physicalId)
                 .transform(&PhysicalDisplay::snapshotRef)
@@ -3876,7 +3884,8 @@
                 }));
     }
 
-    if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+    if (const auto id = compositionDisplay->getDisplayIdVariant().and_then(
+                asHalDisplayId<DisplayIdVariant>)) {
         getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
         creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
     }
@@ -3892,11 +3901,12 @@
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
 
-    creationArgs.physicalOrientation =
-            getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
-    ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
-
-    creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+        creationArgs.initialPowerMode = state.initialPowerMode;
+    } else {
+        creationArgs.initialPowerMode =
+                state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    }
 
     creationArgs.requestedRefreshRate = state.requestedRefreshRate;
 
@@ -3920,10 +3930,12 @@
                                              mode.getPeakFps());
     }
 
-    display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
+    display->setLayerFilter(
+            makeLayerFilterForDisplay(display->getDisplayIdVariant(), state.layerStack));
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
+    display->setOptimizationPolicy(state.optimizationPolicy);
     display->setFlags(state.flags);
 
     return display;
@@ -3969,14 +3981,18 @@
         // Virtual displays without a surface are dormant:
         // they have external state (layer stack, projection,
         // etc.) but no internal state (i.e. a DisplayDevice).
+        ALOGD("Not adding dormant virtual display with token %p: %s", displayToken.unsafe_get(),
+              state.displayName.c_str());
         return;
     }
 
     compositionengine::DisplayCreationArgsBuilder builder;
+    std::optional<VirtualDisplayIdVariant> virtualDisplayIdVariantOpt;
     if (const auto& physical = state.physical) {
         builder.setId(physical->id);
     } else {
-        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId));
+        virtualDisplayIdVariantOpt =
+                acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId, builder);
     }
 
     builder.setPixels(resolution);
@@ -3996,10 +4012,10 @@
     getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
 
     if (state.isVirtual()) {
-        const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());
-        LOG_FATAL_IF(!displayId);
-        auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,
-                                                       bqProducer, bqConsumer, state.displayName);
+        LOG_FATAL_IF(!virtualDisplayIdVariantOpt);
+        auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *virtualDisplayIdVariantOpt,
+                                                       state.surface, bqProducer, bqConsumer,
+                                                       state.displayName);
         displaySurface = surface;
         producer = std::move(surface);
     } else {
@@ -4007,18 +4023,17 @@
                  "adding a supported display, but rendering "
                  "surface is provided (%p), ignoring it",
                  state.surface.get());
-        const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
-        LOG_FATAL_IF(!displayId);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         const auto frameBufferSurface =
-                sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer,
+                sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqProducer,
+                                             bqConsumer,
                                              state.physical->activeMode->getResolution(),
                                              ui::Size(maxGraphicsWidth, maxGraphicsHeight));
         displaySurface = frameBufferSurface;
         producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer();
 #else
         displaySurface =
-                sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
+                sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqConsumer,
                                              state.physical->activeMode->getResolution(),
                                              ui::Size(maxGraphicsWidth, maxGraphicsHeight));
         producer = bqProducer;
@@ -4043,13 +4058,16 @@
         incRefreshableDisplays();
     }
 
+    if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+        applyOptimizationPolicy(__func__);
+    }
+
     mDisplays.try_emplace(displayToken, std::move(display));
 
     // For an external display, loadDisplayModes already attempted to select the same mode
     // as DM, but SF still needs to be updated to match.
     // TODO (b/318534874): Let DM decide the initial mode.
-    if (const auto& physical = state.physical;
-        mScheduler && physical && FlagManager::getInstance().connected_display()) {
+    if (const auto& physical = state.physical; mScheduler && physical) {
         const bool isInternalDisplay = mPhysicalDisplays.get(physical->id)
                                                .transform(&PhysicalDisplay::isInternal)
                                                .value_or(false);
@@ -4072,8 +4090,8 @@
     if (display) {
         display->disconnect();
 
-        if (display->isVirtual()) {
-            releaseVirtualDisplay(display->getVirtualId());
+        if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+            releaseVirtualDisplay(*virtualDisplayIdVariant);
         } else {
             mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
         }
@@ -4100,6 +4118,10 @@
             // not be accessible.
         }));
     }
+
+    if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+        applyOptimizationPolicy(__func__);
+    }
 }
 
 void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
@@ -4112,8 +4134,8 @@
     if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
         if (const auto display = getDisplayDeviceLocked(displayToken)) {
             display->disconnect();
-            if (display->isVirtual()) {
-                releaseVirtualDisplay(display->getVirtualId());
+            if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+                releaseVirtualDisplay(*virtualDisplayIdVariant);
             }
 
             if (display->isRefreshable()) {
@@ -4133,7 +4155,7 @@
         if (currentState.physical) {
             const auto display = getDisplayDeviceLocked(displayToken);
             if (!mSkipPowerOnForQuiescent) {
-                setPowerModeInternal(display, hal::PowerMode::ON);
+                setPhysicalDisplayPowerMode(display, hal::PowerMode::ON);
             }
 
             if (display->getPhysicalId() == mActiveDisplayId) {
@@ -4145,8 +4167,8 @@
 
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
         if (currentState.layerStack != drawingState.layerStack) {
-            display->setLayerFilter(
-                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
+            display->setLayerFilter(makeLayerFilterForDisplay(display->getDisplayIdVariant(),
+                                                              currentState.layerStack));
         }
         if (currentState.flags != drawingState.flags) {
             display->setFlags(currentState.flags);
@@ -4317,20 +4339,19 @@
                                                                 std::move(displayInfos),
                                                                 ftl::to_underlying(vsyncId),
                                                                 frameTime.ns()},
-                                         std::move(
-                                                 inputWindowCommands.windowInfosReportedListeners),
+                                         std::move(inputWindowCommands.releaseListeners()),
                                          /* forceImmediateCall= */ visibleWindowsChanged ||
-                                                 !inputWindowCommands.focusRequests.empty());
+                                                 !inputWindowCommands.getFocusRequests().empty());
         } else {
             // If there are listeners but no changes to input windows, call the listeners
             // immediately.
-            for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
+            for (const auto& listener : inputWindowCommands.getListeners()) {
                 if (IInterface::asBinder(listener)->isBinderAlive()) {
                     listener->onWindowInfosReported();
                 }
             }
         }
-        for (const auto& focusRequest : inputWindowCommands.focusRequests) {
+        for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) {
             inputFlinger->setFocusedWindow(focusRequest);
         }
     }});
@@ -4389,7 +4410,7 @@
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
-        if (HalDisplayId::tryCast(display->getId())) {
+        if (asHalDisplayId(display->getDisplayIdVariant())) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
@@ -4893,12 +4914,14 @@
     return applyTransactions(transactions);
 }
 
-bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions)
+        EXCLUDES(mStateLock) {
     Mutex::Autolock lock(mStateLock);
     return applyTransactionsLocked(transactions);
 }
 
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
+        REQUIRES(mStateLock) {
     bool needsTraversal = false;
     // Now apply all transactions.
     for (auto& transaction : transactions) {
@@ -5051,20 +5074,26 @@
                                                      layerName.c_str(), transactionId);
             if (resolvedState.externalTexture) {
                 resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
+                if (FlagManager::getInstance().monitor_buffer_fences()) {
+                    resolvedState.state.bufferData->buffer->getDependencyMonitor()
+                            .addIngress(FenceTime::makeValid(
+                                                resolvedState.state.bufferData->acquireFence),
+                                        "Incoming txn");
+                }
             }
             mBufferCountTracker.increment(resolvedState.layerId);
         }
         if (resolvedState.state.what & layer_state_t::eReparent) {
-            resolvedState.parentId =
-                    getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+            resolvedState.parentId = getLayerIdFromSurfaceControl(
+                    resolvedState.state.getParentSurfaceControlForChild());
         }
         if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
-            resolvedState.relativeParentId =
-                    getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+            resolvedState.relativeParentId = getLayerIdFromSurfaceControl(
+                    resolvedState.state.getRelativeLayerSurfaceControl());
         }
         if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
             wp<IBinder>& touchableRegionCropHandle =
-                    resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+                    resolvedState.state.editWindowInfo()->touchableRegionCropHandle;
             resolvedState.touchCropId =
                     LayerHandle::getLayerId(touchableRegionCropHandle.promote());
         }
@@ -5115,15 +5144,13 @@
     return NO_ERROR;
 }
 
-bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                                           std::vector<ResolvedComposerState>& states,
-                                           Vector<DisplayState>& displays, uint32_t flags,
-                                           const InputWindowCommands& inputWindowCommands,
-                                           const int64_t desiredPresentTime, bool isAutoTimestamp,
-                                           const std::vector<uint64_t>& uncacheBufferIds,
-                                           const int64_t postTime, bool hasListenerCallbacks,
-                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                           int originPid, int originUid, uint64_t transactionId) {
+bool SurfaceFlinger::applyTransactionState(
+        const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states,
+        Vector<DisplayState>& displays, uint32_t flags,
+        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+        bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock) {
     uint32_t transactionFlags = 0;
 
     // start and end registration for listeners w/ no surface so they can get their callback.  Note
@@ -5275,7 +5302,7 @@
                                                       ResolvedComposerState& composerState,
                                                       int64_t desiredPresentTime,
                                                       bool isAutoTimestamp, int64_t postTime,
-                                                      uint64_t transactionId) {
+                                                      uint64_t transactionId) REQUIRES(mStateLock) {
     layer_state_t& s = composerState.state;
 
     std::vector<ListenerCallbacks> filteredListeners;
@@ -5625,7 +5652,7 @@
 
         // In case of a restart, ensure all displays are off.
         for (const auto& [id, display] : mPhysicalDisplays) {
-            setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
+            setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
         }
 
         // Power on all displays. The primary display is first, so becomes the active display. Also,
@@ -5634,13 +5661,14 @@
         // Additionally, do not turn on displays if the boot should be quiescent.
         if (!mSkipPowerOnForQuiescent) {
             for (const auto& [id, display] : mPhysicalDisplays) {
-                setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+                setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::ON);
             }
         }
     }
 }
 
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
+void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+                                                 hal::PowerMode mode) {
     if (display->isVirtual()) {
         // TODO(b/241285876): This code path should not be reachable, so enforce this at compile
         // time.
@@ -5649,7 +5677,7 @@
     }
 
     const auto displayId = display->getPhysicalId();
-    ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
+    ALOGD("Setting power mode %d on physical display %s", mode, to_string(displayId).c_str());
 
     const auto currentMode = display->getPowerMode();
     if (currentMode == mode) {
@@ -5676,7 +5704,13 @@
         incRefreshableDisplays();
     }
 
+    if (displayId == mActiveDisplayId &&
+        FlagManager::getInstance().correct_virtual_display_power_state()) {
+        applyOptimizationPolicy(__func__);
+    }
+
     const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
+    using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
     if (currentMode == hal::PowerMode::OFF) {
         // Turn on the display
 
@@ -5691,27 +5725,20 @@
             onActiveDisplayChangedLocked(activeDisplay.get(), *display);
         }
 
-        if (displayId == mActiveDisplayId) {
-            // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
-            // set it before SCHED_FIFO due to b/190237315.
-            if (setSchedAttr(true) != NO_ERROR) {
-                ALOGW("Failed to set uclamp.min after powering on active display: %s",
-                      strerror(errno));
-            }
-            if (setSchedFifo(true) != NO_ERROR) {
-                ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
-                      strerror(errno));
-            }
+        if (displayId == mActiveDisplayId &&
+            !FlagManager::getInstance().correct_virtual_display_power_state()) {
+            optimizeThreadScheduling("setPhysicalDisplayPowerMode(ON/DOZE)",
+                                     OptimizationPolicy::optimizeForPerformance);
         }
 
         getHwComposer().setPowerMode(displayId, mode);
-        if (mode != hal::PowerMode::DOZE_SUSPEND &&
-            (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+        if (mode != hal::PowerMode::DOZE_SUSPEND) {
             const bool enable =
                     mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
             requestHardwareVsync(displayId, enable);
 
-            if (displayId == mActiveDisplayId) {
+            if (displayId == mActiveDisplayId &&
+                !FlagManager::getInstance().correct_virtual_display_power_state()) {
                 mScheduler->enableSyntheticVsync(false);
             }
 
@@ -5728,24 +5755,18 @@
             if (const auto display = getActivatableDisplay()) {
                 onActiveDisplayChangedLocked(activeDisplay.get(), *display);
             } else {
-                if (setSchedFifo(false) != NO_ERROR) {
-                    ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
-                          strerror(errno));
-                }
-                if (setSchedAttr(false) != NO_ERROR) {
-                    ALOGW("Failed set uclamp.min after powering off active display: %s",
-                          strerror(errno));
+                if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
+                    optimizeThreadScheduling("setPhysicalDisplayPowerMode(OFF)",
+                                             OptimizationPolicy::optimizeForPower);
                 }
 
-                if (currentModeNotDozeSuspend) {
-                    if (!FlagManager::getInstance().multithreaded_present()) {
-                        mScheduler->disableHardwareVsync(displayId, true);
-                    }
+                if (currentModeNotDozeSuspend &&
+                    !FlagManager::getInstance().correct_virtual_display_power_state()) {
                     mScheduler->enableSyntheticVsync();
                 }
             }
         }
-        if (currentModeNotDozeSuspend && FlagManager::getInstance().multithreaded_present()) {
+        if (currentModeNotDozeSuspend) {
             constexpr bool kDisallow = true;
             mScheduler->disableHardwareVsync(displayId, kDisallow);
         }
@@ -5763,24 +5784,25 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (currentMode == hal::PowerMode::DOZE_SUSPEND &&
-            (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+        if (currentMode == hal::PowerMode::DOZE_SUSPEND) {
             if (displayId == mActiveDisplayId) {
                 ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
                 mVisibleRegionsDirty = true;
                 scheduleRepaint();
-                mScheduler->enableSyntheticVsync(false);
+                if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
+                    mScheduler->enableSyntheticVsync(false);
+                }
             }
             constexpr bool kAllowToEnable = true;
             mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
-        if (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present()) {
-            constexpr bool kDisallow = true;
-            mScheduler->disableHardwareVsync(displayId, kDisallow);
-        }
-        if (displayId == mActiveDisplayId) {
+        constexpr bool kDisallow = true;
+        mScheduler->disableHardwareVsync(displayId, kDisallow);
+
+        if (displayId == mActiveDisplayId &&
+            !FlagManager::getInstance().correct_virtual_display_power_state()) {
             mScheduler->enableSyntheticVsync();
         }
         getHwComposer().setPowerMode(displayId, mode);
@@ -5796,21 +5818,97 @@
 
     mScheduler->setDisplayPowerMode(displayId, mode);
 
-    ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
+    ALOGD("Finished setting power mode %d on physical display %s", mode,
+          to_string(displayId).c_str());
+}
+
+void SurfaceFlinger::setVirtualDisplayPowerMode(const sp<DisplayDevice>& display,
+                                                hal::PowerMode mode) {
+    if (!display->isVirtual()) {
+        ALOGE("%s: Invalid operation on physical display", __func__);
+        return;
+    }
+
+    const auto displayId = display->getVirtualId();
+    ALOGD("Setting power mode %d on virtual display %s %s", mode, to_string(displayId).c_str(),
+          display->getDisplayName().c_str());
+
+    display->setPowerMode(static_cast<hal::PowerMode>(mode));
+
+    applyOptimizationPolicy(__func__);
+
+    ALOGD("Finished setting power mode %d on virtual display %s", mode,
+          to_string(displayId).c_str());
+}
+
+void SurfaceFlinger::optimizeThreadScheduling(
+        const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+    ALOGD("%s: Optimizing thread scheduling: %s", whence, to_string(optimizationPolicy));
+
+    const bool optimizeForPerformance =
+            optimizationPolicy == gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
+    // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall
+    // and set it before SCHED_FIFO due to b/190237315.
+    setSchedAttr(optimizeForPerformance, whence);
+    setSchedFifo(optimizeForPerformance, whence);
+}
+
+void SurfaceFlinger::applyOptimizationPolicy(const char* whence) {
+    using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
+
+    const bool optimizeForPerformance =
+            std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
+                const auto& display = pair.second;
+                return display->isPoweredOn() &&
+                        display->getOptimizationPolicy() ==
+                        OptimizationPolicy::optimizeForPerformance;
+            });
+
+    optimizeThreadScheduling(whence,
+                             optimizeForPerformance ? OptimizationPolicy::optimizeForPerformance
+                                                    : OptimizationPolicy::optimizeForPower);
+
+    if (mScheduler) {
+        const bool disableSyntheticVsync =
+                std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
+                    const auto& display = pair.second;
+                    const hal::PowerMode powerMode = display->getPowerMode();
+                    return powerMode != hal::PowerMode::OFF &&
+                            powerMode != hal::PowerMode::DOZE_SUSPEND &&
+                            display->getOptimizationPolicy() ==
+                            OptimizationPolicy::optimizeForPerformance;
+                });
+        mScheduler->enableSyntheticVsync(!disableSyntheticVsync);
+    }
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
-                                               kMainThreadContext) {
+    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
         mSkipPowerOnForQuiescent = false;
-        const auto display = getDisplayDeviceLocked(displayToken);
+        const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
         if (!display) {
-            ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
-                  displayToken.get());
+            Mutex::Autolock lock(mStateLock);
+            const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+            if (index >= 0) {
+                auto& state = mCurrentState.displays.editValueFor(displayToken);
+                if (state.isVirtual()) {
+                    ALOGD("Setting power mode %d for a dormant virtual display with token %p", mode,
+                          displayToken.get());
+                    state.initialPowerMode = static_cast<hal::PowerMode>(mode);
+                    return;
+                }
+            }
+            ALOGE("Failed to set power mode %d for display token %p", mode, displayToken.get());
         } else if (display->isVirtual()) {
-            ALOGW("Attempt to set power mode %d for virtual display", mode);
+            if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+                ftl::FakeGuard guard(mStateLock);
+                setVirtualDisplayPowerMode(display, static_cast<hal::PowerMode>(mode));
+            } else {
+                ALOGW("Attempt to set power mode %d for virtual display", mode);
+            }
         } else {
-            setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
+            ftl::FakeGuard guard(mStateLock);
+            setPhysicalDisplayPowerMode(display, static_cast<hal::PowerMode>(mode));
         }
     });
 
@@ -6004,17 +6102,14 @@
 
     for (const auto& [token, display] : mDisplays) {
         if (display->isVirtual()) {
-            const auto displayId = display->getId();
+            const VirtualDisplayId virtualId = display->getVirtualId();
             utils::Dumper::Section section(dumper,
-                                           ftl::Concat("Virtual Display ", displayId.value).str());
+                                           ftl::Concat("Virtual Display ", virtualId.value).str());
             display->dump(dumper);
 
-            if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
-                std::lock_guard lock(mVirtualDisplaysMutex);
-                const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
-                if (virtualSnapshotIt != mVirtualDisplays.end()) {
-                    virtualSnapshotIt->second.dump(dumper);
-                }
+            std::lock_guard lock(mVirtualDisplaysMutex);
+            if (const auto snapshotOpt = mVirtualDisplays.get(virtualId)) {
+                snapshotOpt->get().dump(dumper);
             }
         }
     }
@@ -6022,10 +6117,11 @@
 
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+        const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
         if (!displayId) {
             continue;
         }
+
         const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
         if (!hwcDisplayId) {
             continue;
@@ -6034,6 +6130,7 @@
         StringAppendF(&result,
                       "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
                       *hwcDisplayId);
+
         uint8_t port;
         DisplayIdentificationData data;
         if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
@@ -6061,6 +6158,19 @@
         result.append(edid->displayName.data(), edid->displayName.length());
         result.append("\"\n");
     }
+
+    for (const auto& [token, display] : mDisplays) {
+        const auto virtualDisplayId = asVirtualDisplayId(display->getDisplayIdVariant());
+        if (virtualDisplayId) {
+            StringAppendF(&result, "Display %s (Virtual display): displayName=\"%s\"",
+                          to_string(*virtualDisplayId).c_str(), display->getDisplayName().c_str());
+            std::lock_guard lock(mVirtualDisplaysMutex);
+            if (const auto snapshotOpt = mVirtualDisplays.get(*virtualDisplayId)) {
+                StringAppendF(&result, " uniqueId=\"%s\"", snapshotOpt->get().uniqueId().c_str());
+            }
+            result.append("\n");
+        }
+    }
 }
 
 void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args,
@@ -6231,7 +6341,7 @@
 
 void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = HalDisplayId::tryCast(display->getId());
+        const auto displayId = asHalDisplayId(display->getDisplayIdVariant());
         if (!displayId) {
             continue;
         }
@@ -7198,7 +7308,7 @@
     return PERMISSION_DENIED;
 }
 
-status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+void SurfaceFlinger::setSchedFifo(bool enabled, const char* whence) {
     static constexpr int kFifoPriority = 2;
     static constexpr int kOtherPriority = 0;
 
@@ -7213,19 +7323,19 @@
     }
 
     if (sched_setscheduler(0, sched_policy, &param) != 0) {
-        return -errno;
+        const char* kPolicy[] = {"SCHED_OTHER", "SCHED_FIFO"};
+        ALOGW("%s: Failed to set %s: %s", whence, kPolicy[sched_policy == SCHED_FIFO],
+              strerror(errno));
     }
-
-    return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setSchedAttr(bool enabled) {
+void SurfaceFlinger::setSchedAttr(bool enabled, const char* whence) {
     static const unsigned int kUclampMin =
             base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U);
 
     if (!kUclampMin) {
         // uclamp.min set to 0 (default), skip setting
-        return NO_ERROR;
+        return;
     }
 
     sched_attr attr = {};
@@ -7236,10 +7346,9 @@
     attr.sched_util_max = 1024;
 
     if (syscall(__NR_sched_setattr, 0, &attr, 0)) {
-        return -errno;
+        const char* kAction[] = {"disable", "enable"};
+        ALOGW("%s: Failed to %s uclamp.min: %s", whence, kAction[enabled], strerror(errno));
     }
-
-    return NO_ERROR;
 }
 
 namespace {
@@ -7299,7 +7408,7 @@
     }
 
     wp<const DisplayDevice> displayWeak;
-    DisplayId displayId;
+    ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
     ui::LayerStack layerStack;
     ui::Size reqSize(args.width, args.height);
     std::unordered_set<uint32_t> excludeLayerIds;
@@ -7315,7 +7424,7 @@
             return;
         }
         displayWeak = display;
-        displayId = display->getId();
+        displayIdVariantOpt = display->getDisplayIdVariant();
         layerStack = display->getLayerStack();
         displayIsSecure = display->isSecure();
 
@@ -7343,7 +7452,7 @@
 
     ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = displayId;
+    screenshotArgs.displayIdVariant = displayIdVariantOpt;
     screenshotArgs.sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop);
     if (screenshotArgs.sourceCrop.isEmpty()) {
         screenshotArgs.sourceCrop = layerStackSpaceRect;
@@ -7362,6 +7471,7 @@
                                     const sp<IScreenCaptureListener>& captureListener) {
     ui::LayerStack layerStack;
     wp<const DisplayDevice> displayWeak;
+    ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
     ui::Size size;
     Rect layerStackSpaceRect;
     bool displayIsSecure;
@@ -7377,6 +7487,7 @@
         }
 
         displayWeak = display;
+        displayIdVariantOpt = display->getDisplayIdVariant();
         layerStack = display->getLayerStack();
         layerStackSpaceRect = display->getLayerStackSpaceRect();
         size = display->getLayerStackSpaceRect().getSize();
@@ -7411,7 +7522,7 @@
 
     ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = displayId;
+    screenshotArgs.displayIdVariant = displayIdVariantOpt;
     screenshotArgs.sourceCrop = layerStackSpaceRect;
     screenshotArgs.reqSize = size;
     screenshotArgs.dataspace = static_cast<ui::Dataspace>(args.dataspace);
@@ -7820,15 +7931,16 @@
     // Otherwise for seamless transitions it's important to match the current
     // display state as the buffer will be shown under these same conditions, and we
     // want to avoid any flickers.
-    if (captureResults.capturedHdrLayers && !enableLocalTonemapping &&
-        args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) {
-        // Restrict the amount of HDR "headroom" in the screenshot to avoid
-        // over-dimming the SDR portion. 2.0 chosen by experimentation
-        constexpr float kMaxScreenshotHeadroom = 2.0f;
-        // TODO: Aim to update displayBrightnessNits earlier in screenshot
-        // path so ScreenshotArgs can be passed as const
-        args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom,
-                                              args.displayBrightnessNits);
+    if (captureResults.capturedHdrLayers) {
+        if (!enableLocalTonemapping && args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) {
+            // Restrict the amount of HDR "headroom" in the screenshot to avoid
+            // over-dimming the SDR portion. 2.0 chosen by experimentation
+            constexpr float kMaxScreenshotHeadroom = 2.0f;
+            // TODO: Aim to update displayBrightnessNits earlier in screenshot
+            // path so ScreenshotArgs can be passed as const
+            args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom,
+                                                  args.displayBrightnessNits);
+        }
     } else {
         args.displayBrightnessNits = args.sdrWhitePointNits;
     }
@@ -7902,7 +8014,7 @@
                                         .layerStack = layerStack,
                                         .sourceCrop = args.sourceCrop,
                                         .buffer = std::move(buffer),
-                                        .displayId = args.displayId,
+                                        .displayIdVariant = args.displayIdVariant,
                                         .reqBufferSize = args.reqSize,
                                         .sdrWhitePointNits = args.sdrWhitePointNits,
                                         .displayBrightnessNits = args.displayBrightnessNits,
@@ -8236,11 +8348,13 @@
 
 int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                     std::chrono::nanoseconds presentLatency) {
-    auto pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
+    int64_t pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
     if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
         pipelineDepth++;
     }
-    return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
+    const int64_t maxAcquiredBuffers =
+            std::min(pipelineDepth - 1, maxAcquiredBuffersOpt.value_or(pipelineDepth - 1));
+    return std::max(minAcquiredBuffers, maxAcquiredBuffers);
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -8300,8 +8414,8 @@
     // TODO(b/255635821): Choose the pacesetter display, considering both internal and external
     // displays. For now, pick the other internal display, assuming a dual-display foldable.
     return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
-        const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
-        return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+        const auto idOpt = asPhysicalDisplayId(display.getDisplayIdVariant());
+        return idOpt.has_value() && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
                 mPhysicalDisplays.get(*idOpt)
                         .transform(&PhysicalDisplay::isInternal)
                         .value_or(false);
@@ -8358,10 +8472,6 @@
 
 void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel,
                                       int32_t maxLevel) {
-    if (!FlagManager::getInstance().connected_display()) {
-        return;
-    }
-
     Mutex::Autolock lock(mStateLock);
 
     const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -8745,16 +8855,16 @@
     }
 }
 
-binder::Status SurfaceComposerAIDL::createVirtualDisplay(const std::string& displayName,
-                                                         bool isSecure, const std::string& uniqueId,
-                                                         float requestedRefreshRate,
-                                                         sp<IBinder>* outDisplay) {
+binder::Status SurfaceComposerAIDL::createVirtualDisplay(
+        const std::string& displayName, bool isSecure,
+        gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+        float requestedRefreshRate, sp<IBinder>* outDisplay) {
     status_t status = checkAccessPermission();
     if (status != OK) {
         return binderStatusFromStatusT(status);
     }
-    *outDisplay =
-            mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, requestedRefreshRate);
+    *outDisplay = mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy,
+                                                 uniqueId, requestedRefreshRate);
     return binder::Status::ok();
 }
 
@@ -8842,6 +8952,7 @@
     if (status == NO_ERROR) {
         // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
         outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
+        outInfo->port = info.port;
         outInfo->density = info.density;
         outInfo->secure = info.secure;
         outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3f454ba..c472c4c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -160,6 +160,7 @@
 class OutputLayer;
 
 struct CompositionRefreshArgs;
+class DisplayCreationArgsBuilder;
 } // namespace compositionengine
 
 namespace renderengine {
@@ -211,11 +212,9 @@
     SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
     explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
 
-    // set main thread scheduling policy
-    static status_t setSchedFifo(bool enabled) ANDROID_API;
-
-    // set main thread scheduling attributes
-    static status_t setSchedAttr(bool enabled);
+    // Set scheduling policy and attributes of main thread.
+    static void setSchedFifo(bool enabled, const char* whence);
+    static void setSchedAttr(bool enabled, const char* whence);
 
     static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
 
@@ -240,6 +239,11 @@
     // ISurfaceComposer.getMaxAcquiredBufferCount().
     static int64_t minAcquiredBuffers;
 
+    // Controls the maximum acquired buffers SurfaceFlinger will suggest via
+    // ISurfaceComposer.getMaxAcquiredBufferCount().
+    // Value is set through ro.surface_flinger.max_acquired_buffers.
+    static std::optional<int64_t> maxAcquiredBuffersOpt;
+
     // Controls the maximum width and height in pixels that the graphics pipeline can support for
     // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
     static uint32_t maxGraphicsWidth;
@@ -531,6 +535,7 @@
 
     // ISurfaceComposer implementation:
     sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+                                     gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
                                      const std::string& uniqueId,
                                      float requestedRefreshRate = 0.0f);
     status_t destroyVirtualDisplay(const sp<IBinder>& displayToken);
@@ -729,8 +734,19 @@
     void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext);
 
     // Called on the main thread in response to setPowerMode()
-    void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
+    void setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock, kMainThreadContext);
+    void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
+            REQUIRES(mStateLock, kMainThreadContext);
+
+    // Adjusts thread scheduling according to the optimization policy
+    static void optimizeThreadScheduling(
+            const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
+
+    // Enables or disables power optimizations depending on whether there are displays that should
+    // be optimized for performance.
+    void applyOptimizationPolicy(const char* whence) REQUIRES(kMainThreadContext)
+            REQUIRES(mStateLock);
 
     // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
     // display. Falls back to the display's defaultModeId otherwise.
@@ -871,7 +887,7 @@
         std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant;
 
         // Display ID of the display the result will be on
-        std::optional<DisplayId> displayId{std::nullopt};
+        ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt};
 
         // If true, transform is inverted from the parent layer snapshot
         bool childrenOnly{false};
@@ -1025,10 +1041,10 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty);
 
-    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+    ui::LayerFilter makeLayerFilterForDisplay(DisplayIdVariant displayId, ui::LayerStack layerStack)
             REQUIRES(mStateLock) {
         return {layerStack,
-                PhysicalDisplayId::tryCast(displayId)
+                asPhysicalDisplayId(displayId)
                         .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
                         .transform(&display::PhysicalDisplay::isInternal)
                         .value_or(false)};
@@ -1067,7 +1083,8 @@
     // Returns the active mode ID, or nullopt on hotplug failure.
     std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId,
                                                        DisplayIdentificationInfo&&,
-                                                       const char* displayString)
+                                                       const char* displayString,
+                                                       HWComposer::HotplugEvent event)
             REQUIRES(mStateLock, kMainThreadContext);
     void processHotplugDisconnect(PhysicalDisplayId, const char* displayString)
             REQUIRES(mStateLock, kMainThreadContext);
@@ -1120,8 +1137,10 @@
     void enableHalVirtualDisplays(bool);
 
     // Virtual display lifecycle for ID generation and HAL allocation.
-    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId)
-            REQUIRES(mStateLock);
+    std::optional<VirtualDisplayIdVariant> acquireVirtualDisplay(
+            ui::Size, ui::PixelFormat, const std::string& uniqueId,
+            compositionengine::DisplayCreationArgsBuilder&) REQUIRES(mStateLock);
+
     template <typename ID>
     void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
         std::lock_guard lock(mVirtualDisplaysMutex);
@@ -1132,7 +1151,7 @@
         }
     }
 
-    void releaseVirtualDisplay(VirtualDisplayId);
+    void releaseVirtualDisplay(VirtualDisplayIdVariant displayId);
     void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
 
     // Returns a display other than `mActiveDisplayId` that can be activated, if any.
@@ -1217,7 +1236,7 @@
 
     bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
 
-    ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
+    ui::Rotation getPhysicalDisplayOrientation(PhysicalDisplayId, bool isPrimary) const
             REQUIRES(mStateLock);
     void traverseLegacyLayers(const LayerVector::Visitor& visitor) const
             REQUIRES(kMainThreadContext);
@@ -1440,8 +1459,6 @@
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
 
-    // Tracks the number of maximum queued buffers by layer owner Uid.
-    using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
     BufferCountTracker mBufferCountTracker;
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
@@ -1564,9 +1581,11 @@
             const sp<IBinder>& layerHandle,
             sp<gui::IDisplayEventConnection>* outConnection) override;
     binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
-    binder::Status createVirtualDisplay(const std::string& displayName, bool isSecure,
-                                        const std::string& uniqueId, float requestedRefreshRate,
-                                        sp<IBinder>* outDisplay) override;
+    binder::Status createVirtualDisplay(
+            const std::string& displayName, bool isSecure,
+            gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
+            const std::string& uniqueId, float requestedRefreshRate,
+            sp<IBinder>* outDisplay) override;
     binder::Status destroyVirtualDisplay(const sp<IBinder>& displayToken) override;
     binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
     binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 2676ca6..3297c16 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -139,7 +139,8 @@
         colorProto->set_b(layer.color.b);
     }
     if (layer.what & layer_state_t::eTransparentRegionChanged) {
-        LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+        LayerProtoHelper::writeToProto(layer.getTransparentRegion(),
+                                       proto.mutable_transparent_region());
     }
     if (layer.what & layer_state_t::eBufferTransformChanged) {
         proto.set_transform(layer.bufferTransform);
@@ -191,33 +192,30 @@
     }
 
     if (layer.what & layer_state_t::eInputInfoChanged) {
-        if (layer.windowInfoHandle) {
-            const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
-            perfetto::protos::LayerState_WindowInfo* windowInfoProto =
-                    proto.mutable_window_info_handle();
-            windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
-            windowInfoProto->set_layout_params_type(
-                    static_cast<int32_t>(inputInfo->layoutParamsType));
-            windowInfoProto->set_input_config(inputInfo->inputConfig.get());
-            LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
-                                           windowInfoProto->mutable_touchable_region());
-            windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
-            windowInfoProto->set_focusable(
-                    !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
-            windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
-                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
-            windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
-            perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
-            transformProto->set_dsdx(inputInfo->transform.dsdx());
-            transformProto->set_dtdx(inputInfo->transform.dtdx());
-            transformProto->set_dtdy(inputInfo->transform.dtdy());
-            transformProto->set_dsdy(inputInfo->transform.dsdy());
-            transformProto->set_tx(inputInfo->transform.tx());
-            transformProto->set_ty(inputInfo->transform.ty());
-            windowInfoProto->set_replace_touchable_region_with_crop(
-                    inputInfo->replaceTouchableRegionWithCrop);
-            windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
-        }
+        const gui::WindowInfo* inputInfo = &layer.getWindowInfo();
+        perfetto::protos::LayerState_WindowInfo* windowInfoProto =
+                proto.mutable_window_info_handle();
+        windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
+        windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->layoutParamsType));
+        windowInfoProto->set_input_config(inputInfo->inputConfig.get());
+        LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+                                       windowInfoProto->mutable_touchable_region());
+        windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+        windowInfoProto->set_focusable(
+                !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
+        windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
+                gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
+        windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+        perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
+        transformProto->set_dsdx(inputInfo->transform.dsdx());
+        transformProto->set_dtdx(inputInfo->transform.dtdx());
+        transformProto->set_dtdy(inputInfo->transform.dtdy());
+        transformProto->set_dsdy(inputInfo->transform.dsdy());
+        transformProto->set_tx(inputInfo->transform.tx());
+        transformProto->set_ty(inputInfo->transform.ty());
+        windowInfoProto->set_replace_touchable_region_with_crop(
+                inputInfo->replaceTouchableRegionWithCrop);
+        windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
     }
     if (layer.what & layer_state_t::eBackgroundColorChanged) {
         proto.set_bg_color_alpha(layer.bgColor.a);
@@ -410,7 +408,9 @@
         layer.color.b = colorProto.b();
     }
     if (proto.what() & layer_state_t::eTransparentRegionChanged) {
-        LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+        Region transparentRegion;
+        LayerProtoHelper::readFromProto(proto.transparent_region(), transparentRegion);
+        layer.updateTransparentRegion(transparentRegion);
     }
     if (proto.what() & layer_state_t::eBufferTransformChanged) {
         layer.bufferTransform = proto.transform();
@@ -486,7 +486,7 @@
                 windowInfoProto.replace_touchable_region_with_crop();
         resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
 
-        layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+        *layer.editWindowInfo() = inputInfo;
     }
     if (proto.what() & layer_state_t::eBackgroundColorChanged) {
         layer.bgColor.a = proto.bg_color_alpha();
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 5cf4244..84d837c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -102,11 +102,10 @@
             QueuedTransactionState transaction = parser.fromProto(entry.transactions(j));
             for (auto& resolvedComposerState : transaction.states) {
                 if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) {
-                    if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test(
+                    if (!resolvedComposerState.state.getWindowInfo().inputConfig.test(
                                 gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
                         // create a fake token since the FE expects a valid token
-                        resolvedComposerState.state.windowInfoHandle->editInfo()->token =
-                                sp<BBinder>::make();
+                        resolvedComposerState.state.editWindowInfo()->token = sp<BBinder>::make();
                     }
                 }
             }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index b22ec66..2e8c8c1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -18,7 +18,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "TransactionCallbackInvoker"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +28,7 @@
 #include "Utils/FenceUtils.h"
 
 #include <binder/IInterface.h>
+#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <utils/RefBase.h>
 
@@ -142,8 +143,17 @@
                                                     handle->transformHint,
                                                     handle->currentMaxAcquiredBufferCount,
                                                     eventStats, handle->previousReleaseCallbackId);
+
         if (handle->bufferReleaseChannel &&
             handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
+            if (FlagManager::getInstance().monitor_buffer_fences()) {
+                if (auto previousBuffer = handle->previousBuffer.lock()) {
+                    previousBuffer->getBuffer()
+                            ->getDependencyMonitor()
+                            .addEgress(FenceTime::makeValid(handle->previousReleaseFence),
+                                       "Txn release");
+                }
+            }
             mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
                                          handle->previousReleaseCallbackId,
                                          handle->previousReleaseFence,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 178ddbb..34f6ffc 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -25,6 +25,7 @@
 #include <ftl/future.h>
 #include <gui/BufferReleaseChannel.h>
 #include <gui/ITransactionCompletedListener.h>
+#include <renderengine/ExternalTexture.h>
 #include <ui/Fence.h>
 #include <ui/FenceResult.h>
 
@@ -55,6 +56,7 @@
     uint64_t previousFrameNumber = 0;
     ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
+    std::weak_ptr<renderengine::ExternalTexture> previousBuffer;
 };
 
 class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 8786f6e..13f6577 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -16,9 +16,9 @@
     ],
     shared_libs: [
         "libSurfaceFlingerProp",
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
         "librenderengine_includes",
@@ -37,11 +37,12 @@
         "libsurfaceflinger_common_defaults",
     ],
     static_libs: [
-        "libsurfaceflingerflags",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflingerflags",
     ],
 }
 
@@ -51,44 +52,47 @@
         "libsurfaceflinger_common_defaults",
     ],
     static_libs: [
-        "libsurfaceflingerflags_test",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflingerflags_test",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_common_deps",
     shared_libs: [
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
-        "libsurfaceflinger_common",
-        "libsurfaceflingerflags",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflinger_common",
+        "libsurfaceflingerflags",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_common_test_deps",
     shared_libs: [
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
-        "libsurfaceflinger_common_test",
-        "libsurfaceflingerflags_test",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflinger_common_test",
+        "libsurfaceflingerflags_test",
     ],
 }
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index f9aba9f..bf10351 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,8 +26,9 @@
 #include <server_configurable_flags/get_flags.h>
 #include <cinttypes>
 
-#include <android_os.h>
+#include <android_companion_virtualdevice_flags.h>
 #include <android_hardware_flags.h>
+#include <android_os.h>
 #include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <com_android_server_display_feature_flags.h>
@@ -125,9 +126,12 @@
     DUMP_ACONFIG_FLAG(adpf_gpu_sf);
     DUMP_ACONFIG_FLAG(adpf_native_session_manager);
     DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+    DUMP_ACONFIG_FLAG(correct_virtual_display_power_state);
     DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
     DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
+    DUMP_ACONFIG_FLAG(monitor_buffer_fences);
     DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
+    DUMP_ACONFIG_FLAG(vsync_predictor_recovery);
 
     /// Trunk stable readonly flags ///
     /// IMPORTANT - please keep alphabetize to reduce merge conflicts
@@ -138,7 +142,6 @@
     DUMP_ACONFIG_FLAG(begone_bright_hlg);
     DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
     DUMP_ACONFIG_FLAG(commit_not_composited);
-    DUMP_ACONFIG_FLAG(connected_display);
     DUMP_ACONFIG_FLAG(connected_display_hdr);
     DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
     DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
@@ -162,7 +165,6 @@
     DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
     DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
     DUMP_ACONFIG_FLAG(misc1);
-    DUMP_ACONFIG_FLAG(multithreaded_present);
     DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
     DUMP_ACONFIG_FLAG(override_trusted_overlay);
     DUMP_ACONFIG_FLAG(protected_if_client);
@@ -251,14 +253,12 @@
 /// Trunk stable readonly flags ///
 FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
 FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum")
-FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
 FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
 FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
 FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
 FLAG_MANAGER_ACONFIG_FLAG(misc1, "")
 FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config")
 FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "")
-FLAG_MANAGER_ACONFIG_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
 FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "")
 FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "")
 FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved,
@@ -304,9 +304,13 @@
 FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
 FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
 FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, "");
+FLAG_MANAGER_ACONFIG_FLAG(monitor_buffer_fences, "");
+FLAG_MANAGER_ACONFIG_FLAG(vsync_predictor_recovery, "");
 
 /// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(correct_virtual_display_power_state, "",
+                                   android::companion::virtualdevice::flags)
 
 /// Trunk stable readonly flags from outside SurfaceFlinger ///
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index de3f359..8f361ac 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -60,9 +60,12 @@
     bool adpf_native_session_manager() const;
     bool adpf_use_fmq_channel() const;
     bool adpf_use_fmq_channel_fixed() const;
+    bool correct_virtual_display_power_state() const;
     bool graphite_renderengine_preview_rollout() const;
     bool increase_missed_frame_jank_threshold() const;
+    bool monitor_buffer_fences() const;
     bool refresh_rate_overlay_on_external_display() const;
+    bool vsync_predictor_recovery() const;
 
     /// Trunk stable readonly flags ///
     /// IMPORTANT - please keep alphabetize to reduce merge conflicts
@@ -73,7 +76,6 @@
     bool begone_bright_hlg() const;
     bool cache_when_source_crop_layer_only_moved() const;
     bool commit_not_composited() const;
-    bool connected_display() const;
     bool connected_display_hdr() const;
     bool correct_dpi_with_display_size() const;
     bool deprecate_frame_tracker() const;
@@ -98,7 +100,6 @@
     bool local_tonemap_screenshots() const;
     bool luts_api() const;
     bool misc1() const;
-    bool multithreaded_present() const;
     bool no_vsyncs_on_screen_off() const;
     bool override_trusted_overlay() const;
     bool protected_if_client() const;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 73dfa9f..4afcd00 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -77,7 +77,7 @@
     }
 }
 
-int main(int, char**) {
+int main() {
     signal(SIGPIPE, SIG_IGN);
 
     hardware::configureRpcThreadpool(1 /* maxThreads */,
@@ -91,9 +91,7 @@
 
     // Set uclamp.min setting on all threads, maybe an overkill but we want
     // to cover important threads like RenderEngine.
-    if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
-        ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
-    }
+    SurfaceFlinger::setSchedAttr(true, __func__);
 
     // The binder threadpool we start will inherit sched policy and priority
     // of (this) creating thread. We want the binder thread pool to have
@@ -160,14 +158,8 @@
 
     startDisplayService(); // dependency on SF getting registered above
 
-    if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
-        ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
-    }
-
-    // run surface flinger in this thread
+    SurfaceFlinger::setSchedFifo(true, __func__);
     flinger->run();
-
-    return 0;
 }
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index fa1da45..d412a19 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -216,6 +216,13 @@
 } # local_tonemap_screenshots
 
 flag {
+  name: "monitor_buffer_fences"
+  namespace: "core_graphics"
+  description: "Monitors fences for each buffer"
+  bug: "360932099"
+} # monitor_buffer_fences
+
+flag {
   name: "no_vsyncs_on_screen_off"
   namespace: "core_graphics"
   description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
@@ -321,6 +328,16 @@
 } # vrr_bugfix_dropped_frame
 
 flag {
+  name: "vsync_predictor_recovery"
+  namespace: "core_graphics"
+  description: "Recover the vsync predictor from bad vsync model"
+  bug: "385059265"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+ } # vsync_predictor_recovery
+
+flag {
   name: "window_blur_kawase2"
   namespace: "core_graphics"
   description: "Flag for using Kawase2 algorithm for window blur"
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 0ad5ac9..bfafb65 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -483,6 +483,16 @@
     prop_name: "ro.surface_flinger.min_acquired_buffers"
 }
 
+# Defines the maximum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+    api_name: "max_acquired_buffers"
+    type: Long
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_acquired_buffers"
+}
+
 # When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
 # buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
 # Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0017300..e2ac233 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -82,6 +82,11 @@
     prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
   }
   prop {
+    api_name: "max_acquired_buffers"
+    type: Long
+    prop_name: "ro.surface_flinger.max_acquired_buffers"
+  }
+  prop {
     api_name: "max_frame_buffer_acquired_buffers"
     type: Long
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 000628f..ad43cdc 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -19,6 +19,9 @@
         <option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="screen-always-on" value="on" />
+    </target_preparer>
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index c95c875..c91f1ea 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -48,7 +48,11 @@
 
                 ui::DisplayMode displayMode;
                 SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
-                const ui::Size& resolution = displayMode.resolution;
+                ui::Size resolution = displayMode.resolution;
+                if (displayState.orientation == ui::Rotation::Rotation90 ||
+                    displayState.orientation == ui::Rotation::Rotation270) {
+                    std::swap(resolution.width, resolution.height);
+                }
 
                 sp<IBinder> vDisplay;
 
@@ -93,8 +97,8 @@
 #else
                 t.setDisplaySurface(vDisplay, producer);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-                t.setDisplayProjection(vDisplay, displayState.orientation,
-                                       Rect(displayState.layerStackSpaceRect), Rect(resolution));
+                t.setDisplayProjection(vDisplay, ui::Rotation::Rotation0, Rect(resolution),
+                                       Rect(resolution));
                 t.setDisplayLayerStack(vDisplay, layerStack);
                 t.setLayerStack(mirrorSc, layerStack);
                 t.apply();
diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
index 0925118..fec6242 100644
--- a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
+++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
@@ -90,5 +90,46 @@
 }
 BENCHMARK(updateClientStatesNoChanges);
 
+static void propagateManyHiddenChildren(benchmark::State& state) {
+    LayerLifecycleManager lifecycleManager;
+    LayerLifecycleManagerHelper helper(lifecycleManager);
+
+    helper.createRootLayer(0);
+    for (uint32_t i = 1; i < 50; ++i) {
+        helper.createLayer(i, i - 1);
+    }
+
+    helper.hideLayer(0);
+
+    LayerHierarchyBuilder hierarchyBuilder;
+    DisplayInfo info;
+    info.info.logicalHeight = 100;
+    info.info.logicalWidth = 100;
+    DisplayInfos displayInfos;
+    displayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+    ShadowSettings globalShadowSettings;
+
+    LayerSnapshotBuilder snapshotBuilder;
+
+    int i = 1;
+    for (auto _ : state) {
+        i++;
+        helper.setAlpha(0, (1 + (i % 255)) / 255.0f);
+
+        if (lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+            hierarchyBuilder.update(lifecycleManager);
+        }
+        LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
+                                        .layerLifecycleManager = lifecycleManager,
+                                        .displays = displayInfos,
+                                        .globalShadowSettings = globalShadowSettings,
+                                        .supportedLayerGenericMetadata = {},
+                                        .genericLayerMetadataKeyMap = {}};
+        snapshotBuilder.update(args);
+        lifecycleManager.commitChanges();
+    }
+}
+BENCHMARK(propagateManyHiddenChildren);
+
 } // namespace
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 7910e77..82390ac 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -265,9 +265,8 @@
 
         transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
         transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+        *inputInfo = {};
         inputInfo->touchableRegion = region;
         inputInfo->token = sp<BBinder>::make();
         mLifecycleManager.applyTransactions(transactions);
@@ -280,9 +279,8 @@
 
         transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
         transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+        *inputInfo = {};
         if (!inputInfo->token) {
             inputInfo->token = sp<BBinder>::make();
         }
@@ -299,9 +297,8 @@
 
         transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
         transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+        *inputInfo = {};
         inputInfo->touchableRegion = region;
         inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
         transactions.back().states.front().touchCropId = touchCropId;
@@ -455,9 +452,8 @@
         transactions.emplace_back();
         transactions.back().states.push_back({});
 
-        transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
         transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+        transactions.back().states.front().state.updateSurfaceDamageRegion(damageRegion);
         mLifecycleManager.applyTransactions(transactions);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 9ece312..c342e1e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -468,7 +468,7 @@
         layer.externalTexture = buffer;
         layer.bufferData->acquireFence = Fence::NO_FENCE;
         layer.dataspace = ui::Dataspace::UNKNOWN;
-        layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH));
+        layer.setSurfaceDamageRegion(Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 7f9296f..75182e5 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -114,7 +114,11 @@
     // Test instances
 
     TestableSurfaceFlinger mFlinger;
+
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+    sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+            sp<compositionengine::mock::DisplaySurface>::make();
+
     sp<GraphicBuffer> mBuffer =
             sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
                                     GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
@@ -179,7 +183,7 @@
 
 template <typename PhysicalDisplay>
 struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
-    static PhysicalDisplayId get() {
+    static DisplayIdVariant get() {
         if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
                                                        ? LEGACY_DISPLAY_TYPE_PRIMARY
@@ -195,12 +199,12 @@
 
 template <VirtualDisplayId::BaseId displayId>
 struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
-    static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+    static DisplayIdVariant get() { return HalVirtualDisplayId(displayId); }
 };
 
 template <>
 struct DisplayIdGetter<GpuVirtualDisplayIdType> {
-    static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+    static DisplayIdVariant get() { return GpuVirtualDisplayId(0); }
 };
 
 template <typename>
@@ -294,6 +298,7 @@
 
         injector.setSecure(static_cast<bool>(SECURE));
         injector.setNativeWindow(test->mNativeWindow);
+        injector.setDisplaySurface(test->mDisplaySurface);
 
         // Creating a DisplayDevice requires getting default dimensions from the
         // native window along with some other initial setup.
@@ -369,9 +374,8 @@
     // Called by tests to inject a HWC display setup
     template <hal::PowerMode kPowerMode = hal::PowerMode::ON>
     static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
-        const auto displayId = DisplayVariant::DISPLAY_ID::get();
-        ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
-        TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+        TestableSurfaceFlinger::FakeHwcDisplayInjector(DisplayVariant::DISPLAY_ID::get(),
+                                                       HWC_DISPLAY_TYPE,
                                                        static_cast<bool>(DisplayVariant::PRIMARY))
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setResolution(DisplayVariant::RESOLUTION)
@@ -658,9 +662,8 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
-        const auto displayId = Base::DISPLAY_ID::get();
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setId(displayId)
+                                     .setId(Base::DISPLAY_ID::get())
                                      .setPixels(Base::RESOLUTION)
                                      .setIsSecure(static_cast<bool>(Base::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
@@ -673,7 +676,12 @@
                                                        ceDisplayArgs);
 
         // Insert display data so that the HWC thinks it created the virtual display.
-        test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+        const auto ceDisplayIdVar = compositionDisplay->getDisplayIdVariant();
+        LOG_ALWAYS_FATAL_IF(!ceDisplayIdVar);
+        EXPECT_EQ(*ceDisplayIdVar, Base::DISPLAY_ID::get());
+        const auto displayId = asHalDisplayId(*ceDisplayIdVar);
+        LOG_ALWAYS_FATAL_IF(!displayId);
+        test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
 
         return compositionDisplay;
     }
@@ -811,8 +819,9 @@
 
 inline DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
                                         ui::Size resolution = ui::Size(1920, 1080)) {
-    return mock::createDisplayMode(modeId, refreshRate, group, resolution,
-                                   PrimaryDisplayVariant::DISPLAY_ID::get());
+    const auto physicalDisplayId = asPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get());
+    LOG_ALWAYS_FATAL_IF(!physicalDisplayId);
+    return mock::createDisplayMode(modeId, refreshRate, group, resolution, *physicalDisplayId);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
index 90e716f..edcb639 100644
--- a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
+++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
@@ -48,8 +48,10 @@
         }
     }
 
-    static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
-    static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+    static inline PhysicalDisplayId kInnerDisplayId =
+            asPhysicalDisplayId(InnerDisplayVariant::DISPLAY_ID::get()).value();
+    static inline PhysicalDisplayId kOuterDisplayId =
+            asPhysicalDisplayId(OuterDisplayVariant::DISPLAY_ID::get()).value();
 
     sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
 };
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 6f15db8..76e01a6 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -23,7 +23,6 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <gui/DisplayEventReceiver.h>
 #include <log/log.h>
 #include <scheduler/VsyncConfig.h>
 #include <utils/Errors.h>
@@ -112,8 +111,6 @@
     void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime);
     void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                             std::vector<FrameRateOverride>);
-    void expectQueuedBufferCountReceivedByConnection(
-            ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount);
 
     void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
                       nsecs_t deadlineTimestamp) {
@@ -147,7 +144,6 @@
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
-    std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders;
 
     std::chrono::nanoseconds mVsyncPeriod;
 
@@ -380,14 +376,6 @@
     EXPECT_EQ(expectedDisplayId, event.header.displayId);
 }
 
-void EventThreadTest::expectQueuedBufferCountReceivedByConnection(
-        ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) {
-    auto args = connectionEventRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    const auto& event = std::get<0>(args.value());
-    EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers);
-}
-
 namespace {
 
 using namespace testing;
@@ -880,63 +868,6 @@
     EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
 }
 
-TEST_F(EventThreadTest, connectionReceivesBufferStuffing) {
-    setupEventThread();
-
-    // Create a connection that will experience buffer stuffing.
-    ConnectionEventRecorder stuffedConnectionEventRecorder{0};
-    sp<MockEventThreadConnection> stuffedConnection =
-            createConnection(stuffedConnectionEventRecorder,
-                             gui::ISurfaceComposer::EventRegistration::modeChanged |
-                                     gui::ISurfaceComposer::EventRegistration::frameRateOverride,
-                             111);
-
-    // Add a connection and buffer count to the list of stuffed Uids that will receive
-    // data in the next vsync event.
-    BufferStuffingMap bufferStuffedUids;
-    bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3);
-    mThread->addBufferStuffedUids(bufferStuffedUids);
-    mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder);
-
-    // Signal that we want the next vsync event to be posted to two connections.
-    mThread->requestNextVsync(mConnection);
-    mThread->requestNextVsync(stuffedConnection);
-    onVSyncEvent(123, 456, 789);
-
-    // Vsync event data contains number of queued buffers.
-    expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0);
-    expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3);
-}
-
-TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) {
-    setupEventThread();
-
-    // Create a connection with the same Uid as another connection.
-    ConnectionEventRecorder secondConnectionEventRecorder{0};
-    sp<MockEventThreadConnection> secondConnection =
-            createConnection(secondConnectionEventRecorder,
-                             gui::ISurfaceComposer::EventRegistration::modeChanged |
-                                     gui::ISurfaceComposer::EventRegistration::frameRateOverride,
-                             mConnectionUid);
-
-    // Add connection Uid and buffer count to the list of stuffed Uids that will receive
-    // data in the next vsync event.
-    BufferStuffingMap bufferStuffedUids;
-    bufferStuffedUids.try_emplace(mConnectionUid, 3);
-    mThread->addBufferStuffedUids(bufferStuffedUids);
-    mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder);
-    mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder);
-
-    // Signal that we want the next vsync event to be posted to two connections.
-    mThread->requestNextVsync(mConnection);
-    mThread->requestNextVsync(secondConnection);
-    onVSyncEvent(123, 456, 789);
-
-    // Vsync event data contains number of queued buffers.
-    expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3);
-    expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3);
-}
-
 } // namespace
 } // namespace android
 
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index a5b347a..c6da1a1 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -125,13 +125,13 @@
 TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) {
     mFlagManager.setUnitTestMode();
 
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+    SET_FLAG_FOR_TEST(flags::no_vsyncs_on_screen_off, true);
 
     // If this has not been called in this process, it will be called.
     // Regardless, the result is ignored.
     EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(false));
 
-    EXPECT_EQ(true, mFlagManager.multithreaded_present());
+    EXPECT_EQ(true, mFlagManager.no_vsyncs_on_screen_off());
 }
 
 TEST_F(FlagManagerTest, returnsValue) {
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 7aad84b..07356b9 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -261,6 +261,40 @@
     EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
 }
 
+TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHiddenByInvalidTransform) {
+    setMatrix(1, 0, 0, 0, 0);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    setAlpha(1, 0.5);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    setMatrix(1, 1, 0, 0, 1);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
+    EXPECT_EQ(getSnapshot(11)->alpha, 0.5f);
+}
+
+TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHidden) {
+    hideLayer(1);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    setAlpha(1, 0.5);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    showLayer(1);
+    update(mSnapshotBuilder);
+    mLifecycleManager.commitChanges();
+
+    EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
+    EXPECT_EQ(getSnapshot(11)->alpha, 0.5f);
+}
+
 // Change states
 TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
     setCrop(1, Rect(1, 2, 3, 4));
@@ -1642,8 +1676,8 @@
     transactions.back().states.push_back({});
     transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
     transactions.back().states.front().layerId = 3;
-    transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
-    auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+    auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+    *inputInfo = {};
     inputInfo->token = sp<BBinder>::make();
     mLifecycleManager.applyTransactions(transactions);
 
@@ -1671,8 +1705,8 @@
     transactions.back().states.push_back({});
     transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
     transactions.back().states.front().layerId = 3;
-    transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
-    auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+    auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+    *inputInfo = {};
     inputInfo->token = sp<BBinder>::make();
     hideLayer(3);
     mLifecycleManager.applyTransactions(transactions);
@@ -1879,9 +1913,6 @@
 }
 
 TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     setCrop(1, Rect(0, 0, 20, 20));
     setBuffer(1221,
               std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */,
@@ -1920,9 +1951,6 @@
 
 TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) {
     // The left bound is extended when shifting to the right
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     setCrop(1, Rect(0, 0, 20, 20));
     const int texSize = 10;
     setBuffer(1221,
@@ -1942,9 +1970,6 @@
 
 TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) {
     // The right bound is extended when shifting to the left
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     const int crop = 20;
     setCrop(1, Rect(0, 0, crop, crop));
     const int texSize = 10;
@@ -1965,9 +1990,6 @@
 
 TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) {
     // The top bound is extended when shifting to the bottom
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     setCrop(1, Rect(0, 0, 20, 20));
     const int texSize = 10;
     setBuffer(1221,
@@ -1987,9 +2009,6 @@
 
 TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) {
     // The bottom bound is extended when shifting to the top
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     const int crop = 20;
     setCrop(1, Rect(0, 0, crop, crop));
     const int texSize = 10;
@@ -2010,9 +2029,6 @@
 
 TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) {
     // The left bound is extended when shifting to the right
-    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
-        GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
-    }
     const int crop = 20;
     setCrop(1, Rect(0, 0, crop, crop));
     const int texSize = 10;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1fc874d..116fcd9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -143,7 +143,7 @@
                                                                       kDisplay1Mode60->getId()));
 
     // TODO(b/241285191): Restore once VsyncSchedule::getPendingHardwareVsyncState is called by
-    // Scheduler::setDisplayPowerMode rather than SF::setPowerModeInternal.
+    // Scheduler::setDisplayPowerMode rather than SF::setPhysicalDisplayPowerMode.
 #if 0
     // Hardware VSYNC should be disabled for newly registered displays.
     EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId2, false)).Times(1);
@@ -254,18 +254,43 @@
 }
 
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
-    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
-    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
-    EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms));
+    struct TestCase {
+        Fps refreshRate;
+        std::chrono::nanoseconds presentLatency;
+        int expectedBufferCount;
+    };
 
-    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
+    const auto verifyTestCases = [&](std::vector<TestCase> tests) {
+        for (const auto testCase : tests) {
+            EXPECT_EQ(testCase.expectedBufferCount,
+                      mFlinger.calculateMaxAcquiredBufferCount(testCase.refreshRate,
+                                                               testCase.presentLatency));
+        }
+    };
 
-    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+    std::vector<TestCase> testCases{{60_Hz, 30ms, 1},
+                                    {90_Hz, 30ms, 2},
+                                    {120_Hz, 30ms, 3},
+                                    {60_Hz, 40ms, 2},
+                                    {60_Hz, 10ms, 1}};
+    verifyTestCases(testCases);
 
     const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
     mFlinger.mutableMinAcquiredBuffers() = 2;
-    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+    verifyTestCases({{60_Hz, 10ms, 2}});
     mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
+
+    const auto savedMaxAcquiredBuffers = mFlinger.mutableMaxAcquiredBuffers();
+    mFlinger.mutableMaxAcquiredBuffers() = 2;
+    testCases = {{60_Hz, 30ms, 1},
+                 {90_Hz, 30ms, 2},
+                 {120_Hz, 30ms, 2}, // max buffers allowed is 2
+                 {60_Hz, 40ms, 2},
+                 {60_Hz, 10ms, 1}};
+    verifyTestCases(testCases);
+    mFlinger.mutableMaxAcquiredBuffers() = 3; // max buffers allowed is 3
+    verifyTestCases({{120_Hz, 30ms, 3}});
+    mFlinger.mutableMaxAcquiredBuffers() = savedMaxAcquiredBuffers;
 }
 
 MATCHER(Is120Hz, "") {
@@ -716,8 +741,6 @@
 }
 
 TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
-
     // resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on,
     // but not on display 2, which is off.
     EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
@@ -738,28 +761,6 @@
     mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
 }
 
-TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
-
-    // In the legacy code, prior to the flag, resync applied to OFF displays.
-    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
-    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
-
-    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
-
-    mScheduler->registerDisplay(kDisplayId2,
-                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
-                                                                      kDisplay2Mode60->getId()));
-    ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
-
-    static constexpr bool kDisallow = true;
-    mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
-    mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
-
-    static constexpr bool kAllowToEnable = true;
-    mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
-}
-
 class AttachedChoreographerTest : public SchedulerTest {
 protected:
     void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index aadff76..aa48c1b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -50,9 +50,9 @@
         EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
         EXPECT_EQ(name.c_str(), display.displayName);
 
-        const VirtualDisplayId vid = GpuVirtualDisplayId(baseId);
         sp<DisplayDevice> device =
-                mFlinger.createVirtualDisplayDevice(displayToken, vid, requestedRefreshRate);
+                mFlinger.createVirtualDisplayDevice(displayToken, GpuVirtualDisplayId(baseId),
+                                                    requestedRefreshRate);
 
         EXPECT_TRUE(device->isVirtual());
         device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
@@ -141,7 +141,11 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false, kUniqueId);
+    sp<IBinder> displayToken =
+            mFlinger.createVirtualDisplay(kDisplayName, false,
+                                          gui::ISurfaceComposer::OptimizationPolicy::
+                                                  optimizeForPower,
+                                          kUniqueId);
 
     // --------------------------------------------------------------------
     // Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index a506873..3f710fd 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -171,16 +171,22 @@
     static constexpr DisplayModeId kModeId90{1};
     static constexpr DisplayModeId kModeId120{2};
     static constexpr DisplayModeId kModeId90_4K{3};
+    static constexpr DisplayModeId kModeId60_8K{4};
 
     static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz, 0);
     static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz, 1);
     static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz, 2);
 
     static constexpr ui::Size kResolution4K{3840, 2160};
+    static constexpr ui::Size kResolution8K{7680, 4320};
+
     static inline const DisplayModePtr kMode90_4K =
             createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+    static inline const DisplayModePtr kMode60_8K =
+            createDisplayMode(kModeId60_8K, 60_Hz, 4, kResolution8K);
 
-    static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
+    static inline const DisplayModes kModes =
+            makeModes(kMode60, kMode90, kMode120, kMode90_4K, kMode60_8K);
 };
 
 void DisplayModeSwitchingTest::setupScheduler(
@@ -326,6 +332,8 @@
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) {
+    SET_FLAG_FOR_TEST(flags::synced_resolution_switch, false);
+
     EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
 
     EXPECT_EQ(NO_ERROR,
@@ -360,9 +368,45 @@
     EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K));
 }
 
-TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
-    SET_FLAG_FOR_TEST(flags::connected_display, true);
+TEST_F(DisplayModeSwitchingTest, changeResolutionSynced) {
+    SET_FLAG_FOR_TEST(flags::synced_resolution_switch, true);
 
+    EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+    // PrimaryDisplayVariant has a 4K size, so switch to 8K.
+    EXPECT_EQ(NO_ERROR,
+              mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                                  mock::createDisplayModeSpecs(kModeId60_8K,
+                                                                               60_Hz)));
+
+    EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+    // The mode should not be set until the commit that resizes the display.
+    mFlinger.commit();
+    EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+    mFlinger.commit();
+    EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+    // Set the display size to match the resolution.
+    DisplayState state;
+    state.what = DisplayState::eDisplaySizeChanged;
+    state.token = mDisplay->getDisplayToken().promote();
+    state.width = static_cast<uint32_t>(kResolution8K.width);
+    state.height = static_cast<uint32_t>(kResolution8K.height);
+
+    // The next commit should set the mode and resize the framebuffer.
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+    EXPECT_CALL(*mDisplaySurface, resizeBuffers(kResolution8K));
+    EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60_8K);
+
+    constexpr bool kModeset = true;
+    mFlinger.setDisplayStateLocked(state);
+    mFlinger.configureAndCommit(kModeset);
+
+    EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60_8K));
+}
+
+TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
     const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
 
     EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -371,8 +415,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -402,8 +446,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
@@ -427,8 +471,6 @@
 }
 
 TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
-    SET_FLAG_FOR_TEST(flags::connected_display, true);
-
     const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
 
     EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -437,8 +479,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -480,7 +522,7 @@
     EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
 
     // Power off the display before the mode has been set.
-    mFlinger.setPowerModeInternal(mDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mDisplay, hal::PowerMode::OFF);
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -497,8 +539,6 @@
 }
 
 TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
-    SET_FLAG_FOR_TEST(flags::connected_display, true);
-
     const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
 
     EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -507,8 +547,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -525,7 +565,7 @@
     EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     // Power off the outer display before the mode has been set.
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -542,8 +582,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 1335640..eac5a8e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -68,13 +68,9 @@
 
 template <typename Case, bool connected>
 void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
-    const auto convert = [](auto physicalDisplayId) {
-        return std::make_optional(DisplayId{physicalDisplayId});
-    };
-
-    EXPECT_CALL(*eventThread,
-                onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
-            .Times(1);
+    const auto physicalDisplayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(physicalDisplayId);
+    EXPECT_CALL(*eventThread, onHotplugReceived(*physicalDisplayId, connected)).Times(1);
 }
 
 template <typename Case>
@@ -111,7 +107,7 @@
 
     std::optional<DisplayDeviceState::Physical> expectedPhysical;
     if (Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
@@ -137,10 +133,10 @@
     EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
     // SF should have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    const auto displayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayIdOpt);
 
-    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(*displayIdOpt);
     ASSERT_TRUE(displayOpt);
 
     const auto& display = displayOpt->get();
@@ -246,9 +242,9 @@
     EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
     // SF should not have a PhysicalDisplay.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+    const auto physicalDisplayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(physicalDisplayIdOpt);
+    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
 
     // The existing token should have been removed.
     verifyDisplayIsNotConnected(existing.token());
@@ -356,9 +352,10 @@
                 EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
                 // SF should not have a PhysicalDisplay.
-                const auto displayId = Case::Display::DISPLAY_ID::get();
-                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+                const auto physicalDisplayIdOpt =
+                        asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+                ASSERT_TRUE(physicalDisplayIdOpt);
+                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
             }(),
             testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
@@ -400,10 +397,12 @@
 
                 // The existing token should have been removed.
                 verifyDisplayIsNotConnected(existing.token());
-                const auto displayId = Case::Display::DISPLAY_ID::get();
-                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                const auto physicalDisplayIdOpt =
+                        asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+                ASSERT_TRUE(physicalDisplayIdOpt);
 
-                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+                const auto displayOpt =
+                        mFlinger.mutablePhysicalDisplays().get(*physicalDisplayIdOpt);
                 ASSERT_TRUE(displayOpt);
                 EXPECT_NE(existing.token(), displayOpt->get().token());
 
@@ -540,9 +539,9 @@
     // Preconditions
 
     // A virtual display is set up but is removed from the current state.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
-    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+    const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayId);
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
     Case::Display::injectHwcDisplay(this);
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
     existing.inject();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index 19f8deb..8972840 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -38,30 +38,30 @@
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
     // ...and should still be after powering on.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     // The outer display should become the pacesetter after folding.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(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);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     // 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);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
     // The outer display should become the pacesetter if designated.
@@ -74,20 +74,20 @@
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     // The outer display should become the pacesetter if the inner display powers off.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
 
     // The outer display should stay the pacesetter if both are powered on.
     // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
 
     // The inner display should become the pacesetter if the outer display powers off.
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
@@ -114,8 +114,8 @@
             .Times(0);
 
     // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
-    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     EXPECT_TRUE(mInnerDisplay->isPoweredOn());
     EXPECT_FALSE(mOuterDisplay->isPoweredOn());
@@ -133,10 +133,10 @@
             .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);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     EXPECT_FALSE(mInnerDisplay->isPoweredOn());
     EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -154,9 +154,9 @@
             .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);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     EXPECT_TRUE(mInnerDisplay->isPoweredOn());
     EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -167,18 +167,16 @@
 }
 
 TEST_F(FoldableTest, requestVsyncOnPowerOn) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
             .Times(1);
     EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
             .Times(1);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 }
 
 TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     // When the device boots, the inner display should be the pacesetter.
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
@@ -192,10 +190,10 @@
     EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false))
             .Times(1);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
 
     // Other display is now the pacesetter.
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index b0cda0f..b8b1b95 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -90,7 +90,9 @@
     mFlinger.configure();
 
     EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    const auto primaryDisplayId = asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(primaryDisplayId);
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*primaryDisplayId));
     const auto primaryDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(primaryDisplayIdOpt.has_value());
@@ -98,13 +100,15 @@
             mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
     ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
     const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
-    EXPECT_EQ(PrimaryDisplay::DISPLAY_ID::get(), primaryDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(*primaryDisplayId, primaryDisplaySnapshotRef.get().displayId());
     EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port());
     EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
               primaryDisplaySnapshotRef.get().connectionType());
 
     EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+    const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(externalDisplayId);
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
     const auto externalDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(externalDisplayIdOpt.has_value());
@@ -112,7 +116,7 @@
             mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
     ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
     const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
-    EXPECT_EQ(ExternalDisplay::DISPLAY_ID::get(), externalDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(*externalDisplayId, externalDisplaySnapshotRef.get().displayId());
     EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port());
     EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
               externalDisplaySnapshotRef.get().connectionType());
@@ -154,8 +158,8 @@
     constexpr PhysicalDisplayId primaryInternalDisplayId =
             PhysicalDisplayId::fromPort(primaryInternalDisplayPort);
     EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
-    ASSERT_EQ(primaryInternalDisplayId, PrimaryDisplay::DISPLAY_ID::get());
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    ASSERT_EQ(primaryInternalDisplayId, asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get()));
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(primaryInternalDisplayId));
     const auto primaryDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(primaryDisplayIdOpt.has_value());
@@ -220,12 +224,12 @@
     // The display should be scheduled for removal during the next commit. At this point, it should
     // still exist but be marked as disconnected.
     EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
-    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+    const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(externalDisplayId);
+    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
 }
 
 TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
-    SET_FLAG_FOR_TEST(flags::connected_display, true);
-
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
 
@@ -263,8 +267,6 @@
 }
 
 TEST_F(HotplugTest, rejectsHotplugOnActivePortsDuplicate) {
-    SET_FLAG_FOR_TEST(flags::connected_display, true);
-
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
similarity index 82%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
index fed7b2e..2332bf6 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <android_companion_virtualdevice_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <common/test/FlagUtils.h>
 #include "DisplayTransactionTestHelpers.h"
@@ -78,11 +79,19 @@
 struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
     static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+        setupDisableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 
     static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+        setupEnableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 };
@@ -91,12 +100,20 @@
     static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         // Expect to enable hardware VSYNC and disable synthetic VSYNC.
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+        setupDisableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         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.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+        setupEnableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
     }
 };
@@ -151,7 +168,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -176,7 +193,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -188,7 +205,7 @@
 struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -206,7 +223,7 @@
 struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -234,7 +251,7 @@
       : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupNoComposerPowerModeCallExpectations(test);
     }
 };
@@ -315,7 +332,7 @@
                          EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
                          TransitionVariant>;
 
-class SetPowerModeInternalTest : public DisplayTransactionTest {
+class SetPhysicalDisplayPowerModeTest : public DisplayTransactionTest {
 public:
     template <typename Case>
     void transitionDisplayCommon();
@@ -331,15 +348,17 @@
 struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
 
 template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
+void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
+    SET_FLAG_FOR_TEST(android::companion::virtualdevice::flags::correct_virtual_display_power_state,
+                      true);
+
     Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
-    auto displayId = display->getId();
-    if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) {
+    if (auto physicalDisplayId = asPhysicalDisplayId(display->getDisplayIdVariant())) {
         Case::setInitialHwVsyncEnabled(this, *physicalDisplayId,
                                        PowerModeInitialVSyncEnabled<
                                                Case::Transition::INITIAL_POWER_MODE>::value);
@@ -353,7 +372,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
+    mFlinger.setPhysicalDisplayPowerMode(display, Case::Transition::TARGET_POWER_MODE);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -361,7 +380,7 @@
     Case::Transition::verifyPostconditions(this);
 }
 
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfNoChange) {
     using Case = SimplePrimaryDisplayCase;
 
     // --------------------------------------------------------------------
@@ -378,7 +397,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::ON);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -386,16 +405,16 @@
     EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfVirtualDisplay) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
     // Preconditions
 
     // Insert display data so that the HWC thinks it created the virtual display.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
-    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+    const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutableHwcDisplayData().try_emplace(*displayId).second);
 
     // A virtual display device is set up
     Case::Display::injectHwcDisplay(this);
@@ -408,7 +427,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::OFF);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -416,88 +435,83 @@
     EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
-    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index cd554ea..23e73de 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -235,7 +235,7 @@
 
     constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value;
     if constexpr (kConnectionTypeOpt) {
-        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
@@ -282,7 +282,7 @@
     // Postconditions
 
     ASSERT_NE(nullptr, device);
-    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getDisplayIdVariant());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 41f2b6e..c5973db 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -337,9 +337,9 @@
         mFlinger->configure();
     }
 
-    void configureAndCommit() {
+    void configureAndCommit(bool modeset = false) {
         configure();
-        commitTransactionsLocked(eDisplayTransactionNeeded);
+        commitTransactionsLocked(eDisplayTransactionNeeded, modeset);
     }
 
     void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
@@ -399,12 +399,16 @@
                               float requestedRefreshRate = 0.0f) {
         static const std::string kTestId =
                 "virtual:libsurfaceflinger_unittest:TestableSurfaceFlinger";
-        return mFlinger->createVirtualDisplay(displayName, isSecure, kTestId, requestedRefreshRate);
+        return mFlinger
+                ->createVirtualDisplay(displayName, isSecure,
+                                       gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower,
+                                       kTestId, requestedRefreshRate);
     }
 
     auto createVirtualDisplay(const std::string& displayName, bool isSecure,
+                              gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
                               const std::string& uniqueId, float requestedRefreshRate = 0.0f) {
-        return mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId,
+        return mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy, uniqueId,
                                               requestedRefreshRate);
     }
 
@@ -429,11 +433,14 @@
                                                        dispSurface, producer);
     }
 
-    void commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags, bool modeset = false) {
         Mutex::Autolock lock(mFlinger->mStateLock);
         ftl::FakeGuard guard(kMainThreadContext);
         mFlinger->processDisplayChangesLocked();
         mFlinger->commitTransactionsLocked(transactionFlags);
+        if (modeset) {
+            mFlinger->initiateDisplayModeChanges();
+        }
     }
 
     void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) {
@@ -455,9 +462,9 @@
     }
 
     // Allow reading display state without locking, as if called on the SF main thread.
-    auto setPowerModeInternal(const sp<DisplayDevice>& display,
-                              hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->setPowerModeInternal(display, mode);
+    auto setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+                                     hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->setPhysicalDisplayPowerMode(display, mode);
     }
 
     auto renderScreenImpl(const sp<DisplayDevice> display, const Rect sourceCrop,
@@ -474,7 +481,7 @@
 
         SurfaceFlinger::ScreenshotArgs screenshotArgs;
         screenshotArgs.captureTypeVariant = display;
-        screenshotArgs.displayId = std::nullopt;
+        screenshotArgs.displayIdVariant = std::nullopt;
         screenshotArgs.sourceCrop = sourceCrop;
         screenshotArgs.reqSize = sourceCrop.getSize();
         screenshotArgs.dataspace = dataspace;
@@ -576,7 +583,7 @@
     }
 
     sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
-                                                 VirtualDisplayId displayId,
+                                                 GpuVirtualDisplayId displayId,
                                                  float requestedRefreshRate) {
         constexpr ui::Size kResolution = {1080, 1920};
         auto compositionDisplay = compositionengine::impl::
@@ -713,6 +720,7 @@
     }
 
     auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+    auto& mutableMaxAcquiredBuffers() { return SurfaceFlinger::maxAcquiredBuffersOpt; }
     auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
         return mFlinger->mLayerSnapshotBuilder;
     }
@@ -819,9 +827,11 @@
         static constexpr int32_t DEFAULT_DPI = 320;
         static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
 
-        FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+        FakeHwcDisplayInjector(DisplayIdVariant displayIdVariant, hal::DisplayType hwcDisplayType,
                                bool isPrimary)
-              : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
+              : mDisplayIdVariant(displayIdVariant),
+                mHwcDisplayType(hwcDisplayType),
+                mIsPrimary(isPrimary) {}
 
         auto& setHwcDisplayId(hal::HWDisplayId displayId) {
             mHwcDisplayId = displayId;
@@ -886,7 +896,9 @@
 
             display->setPowerMode(mPowerMode);
 
-            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
+            const auto halDisplayId = asHalDisplayId(mDisplayIdVariant);
+            ASSERT_TRUE(halDisplayId);
+            flinger->mutableHwcDisplayData()[*halDisplayId].hwcDisplay = std::move(display);
 
             EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
                     .WillRepeatedly(
@@ -924,9 +936,10 @@
                             DoAll(SetArgPointee<3>(mConfigGroup), Return(hal::Error::NONE)));
 
             if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
-                const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
-                LOG_ALWAYS_FATAL_IF(!physicalId);
-                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
+                const auto physicalDisplayId = asPhysicalDisplayId(mDisplayIdVariant);
+                ASSERT_TRUE(physicalDisplayId);
+                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId,
+                                                                  *physicalDisplayId);
                 if (mIsPrimary) {
                     flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
                 } else {
@@ -939,7 +952,7 @@
         }
 
     private:
-        const HalDisplayId mDisplayId;
+        const DisplayIdVariant mDisplayIdVariant;
         const hal::DisplayType mHwcDisplayType;
         const bool mIsPrimary;
 
@@ -975,8 +988,8 @@
         sp<IBinder> token() const { return mDisplayToken; }
 
         auto physicalDisplay() const {
-            return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
-                    .and_then(&PhysicalDisplayId::tryCast)
+            return mCreationArgs.compositionDisplay->getDisplayIdVariant()
+                    .and_then(asPhysicalDisplayId)
                     .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
         }
 
@@ -1074,7 +1087,9 @@
             DisplayDeviceState state;
             state.isSecure = mCreationArgs.isSecure;
 
-            if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+            if (const auto physicalId =
+                        mCreationArgs.compositionDisplay->getDisplayIdVariant().and_then(
+                                asPhysicalDisplayId)) {
                 LOG_ALWAYS_FATAL_IF(!mConnectionType);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 918107d..ccf6a9c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -304,6 +304,53 @@
     EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
 }
 
+TEST_F(VSyncPredictorTest, recoverAfterDriftedVSyncAreReplacedWithCorrectVSync) {
+    SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true);
+    auto constexpr idealPeriodNs = 4166666;
+    auto constexpr minFrameIntervalNs = 8333333;
+    auto constexpr idealPeriod = Fps::fromPeriodNsecs(idealPeriodNs);
+    auto constexpr minFrameRate = Fps::fromPeriodNsecs(minFrameIntervalNs);
+    hal::VrrConfig vrrConfig{.minFrameIntervalNs = minFrameIntervalNs};
+    ftl::NonNull<DisplayModePtr> mode =
+            ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), idealPeriod, vrrConfig));
+    VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+                              kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+    vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ true);
+    // Curated list of VSyncs that causes the VSync drift.
+    std::vector<nsecs_t> const simulatedVsyncs{74473665741, 74481774375, 74489911818, 74497993491,
+                                               74506000833, 74510002150, 74513904390, 74517748707,
+                                               74521550947, 74525383187, 74529165427, 74533067667,
+                                               74536751484, 74540653724, 74544282649, 74548084889,
+                                               74551917129, 74555699369, 74559601609, 74563601611,
+                                               74567503851, 74571358168, 74575260408, 74578889333,
+                                               74582691573, 74586523813, 74590306053, 74593589870,
+                                               74597492110, 74601121035, 74604923275, 74608755515,
+                                               74612537755, 74616166680, 74619795605, 74623424530,
+                                               74627043455, 74630645695, 74634245935, 74637778175,
+                                               74641291992, 74644794232, 74648275157, 74651575397,
+                                               74654807637, 74658007877, 74661176117, 74664345357};
+    for (auto const& timestamp : simulatedVsyncs) {
+        vrrTracker.addVsyncTimestamp(timestamp);
+    }
+    auto slope = vrrTracker.getVSyncPredictionModel().slope;
+    // Without using the idealPeriod for the calculation of the VSync predictor mode the
+    // the slope would be 3343031
+    EXPECT_THAT(slope, IsCloseTo(4347805, mMaxRoundingError));
+    EXPECT_FALSE(vrrTracker.needsMoreSamples());
+
+    auto lastVsync = 74664345357;
+    // Add valid VSyncs to replace the drifted VSyncs
+    for (int i = 0; i <= kHistorySize; i++) {
+        lastVsync += minFrameIntervalNs;
+        EXPECT_TRUE(vrrTracker.addVsyncTimestamp(lastVsync));
+    }
+    EXPECT_FALSE(vrrTracker.needsMoreSamples());
+    slope = vrrTracker.getVSyncPredictionModel().slope;
+    // Corrected slop is closer to the idealPeriod
+    // when valid vsync are inserted otherwise this would still be 3349673
+    EXPECT_THAT(slope, IsCloseTo(idealPeriodNs, mMaxRoundingError));
+}
+
 TEST_F(VSyncPredictorTest, handlesVsyncChange) {
     auto const fastPeriod = 100;
     auto const fastTimeBase = 100;
@@ -402,11 +449,7 @@
     std::vector<nsecs_t> const simulatedVsyncs{
             158929578733000,
             158929306806205, // oldest TS in ringbuffer
-            158929650879052,
-            158929661969209,
-            158929684198847,
-            158929695268171,
-            158929706370359,
+            158929650879052, 158929661969209, 158929684198847, 158929695268171, 158929706370359,
     };
     auto const idealPeriod = 11111111;
     auto const expectedPeriod = 11113919;
@@ -421,9 +464,9 @@
     EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
     EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
 
-    // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
-    // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
-    // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+    // (timePoint - oldestTS) % expectedPeriod works out to be: 10702663
+    // (timePoint - oldestTS) / expectedPeriod works out to be: 37.96
+    // so failure to account for the offset will floor the ordinal to 37, which was in the past.
     auto const timePoint = 158929728723871;
     auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
     EXPECT_THAT(prediction, Ge(timePoint));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 3036fec..cce4d2a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -55,7 +55,6 @@
     MOCK_METHOD(void, onHdcpLevelsChanged,
                 (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
                 (override));
-    MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override));
 };
 
 } // namespace android::mock
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4735ae5..4b12cc2 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -26,6 +26,7 @@
 
     srcs: [
         "VibratorCallbackScheduler.cpp",
+        "VibratorController.cpp",
         "VibratorHalController.cpp",
         "VibratorHalWrapper.cpp",
         "VibratorManagerHalController.cpp",
@@ -41,22 +42,17 @@
     },
 
     shared_libs: [
+        "android.hardware.vibrator-V3-ndk",
         "libbinder_ndk",
-        "libhidlbase",
         "liblog",
         "libutils",
-        "android.hardware.vibrator-V3-ndk",
-        "android.hardware.vibrator@1.0",
-        "android.hardware.vibrator@1.1",
-        "android.hardware.vibrator@1.2",
-        "android.hardware.vibrator@1.3",
     ],
 
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wunused",
         "-Wunreachable-code",
+        "-Wunused",
     ],
 
     local_include_dirs: ["include"],
diff --git a/services/vibratorservice/VibratorController.cpp b/services/vibratorservice/VibratorController.cpp
new file mode 100644
index 0000000..21924e9
--- /dev/null
+++ b/services/vibratorservice/VibratorController.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2025 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 "VibratorController"
+
+#ifndef qDoWithRetries
+#define qDoWithRetries(op) doWithRetries(op, __FUNCTION__)
+#endif
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorController.h>
+
+using ::aidl::android::hardware::vibrator::Effect;
+using ::aidl::android::hardware::vibrator::EffectStrength;
+using ::aidl::android::hardware::vibrator::IVibrator;
+
+using Status = ::ndk::ScopedAStatus;
+
+using namespace std::placeholders;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+inline bool isStatusUnsupported(const Status& status) {
+    // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported.
+    return status.getStatus() == STATUS_UNKNOWN_TRANSACTION ||
+            status.getExceptionCode() == EX_UNSUPPORTED_OPERATION;
+}
+
+inline bool isStatusTransactionFailed(const Status& status) {
+    // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported.
+    return status.getStatus() != STATUS_UNKNOWN_TRANSACTION &&
+            status.getExceptionCode() == EX_TRANSACTION_FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool VibratorProvider::isDeclared() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mIsDeclared.has_value()) {
+        return *mIsDeclared;
+    }
+
+    bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str());
+    if (!isDeclared) {
+        ALOGV("Vibrator HAL service not declared.");
+    }
+
+    mIsDeclared.emplace(isDeclared);
+    return isDeclared;
+}
+
+std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() {
+    if (!isDeclared()) {
+        return nullptr;
+    }
+
+    auto vibrator = IVibrator::fromBinder(
+            ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str())));
+    if (vibrator) {
+        ALOGV("Successfully connected to Vibrator HAL service.");
+    } else {
+        ALOGE("Error connecting to declared Vibrator HAL service.");
+    }
+
+    return vibrator;
+}
+
+std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() {
+    if (!isDeclared()) {
+        return nullptr;
+    }
+
+    auto vibrator = IVibrator::fromBinder(
+            ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str())));
+    if (vibrator) {
+        ALOGV("Successfully reconnected to Vibrator HAL service.");
+    } else {
+        ALOGE("Error reconnecting to declared Vibrator HAL service.");
+    }
+
+    return vibrator;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool VibratorController::init() {
+    if (!mVibratorProvider->isDeclared()) {
+        return false;
+    }
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mVibrator == nullptr) {
+        mVibrator = mVibratorProvider->waitForVibrator();
+    }
+    return mVibratorProvider->isDeclared();
+}
+
+Status VibratorController::off() {
+    return qDoWithRetries(std::bind(&IVibrator::off, _1));
+}
+
+Status VibratorController::setAmplitude(float amplitude) {
+    return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude));
+}
+
+Status VibratorController::setExternalControl(bool enabled) {
+    return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled));
+}
+
+Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect,
+                                          const EffectStrength& strength) {
+    return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength));
+}
+
+Status VibratorController::alwaysOnDisable(int32_t id) {
+    return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mVibrator = mVibratorProvider->checkForVibrator();
+    return mVibrator;
+}
+
+Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op,
+                                         const char* logLabel) {
+    if (!init()) {
+        ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel);
+        return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared");
+    }
+    std::shared_ptr<IVibrator> vibrator;
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        vibrator = mVibrator;
+    }
+
+    if (!vibrator) {
+        ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel);
+        return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
+                                                    "IVibrator declared but failed to load");
+    }
+
+    auto status = doOnce(vibrator.get(), op, logLabel);
+    for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) {
+        vibrator = reconnectToVibrator();
+        if (!vibrator) {
+            // Failed to reconnect to vibrator HAL after a transaction failed, skip retries.
+            break;
+        }
+        status = doOnce(vibrator.get(), op, logLabel);
+    }
+
+    return status;
+}
+
+Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op,
+                                  const char* logLabel) {
+    auto status = op(vibrator);
+    if (!status.isOk()) {
+        if (isStatusUnsupported(status)) {
+            ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage());
+        } else {
+            ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage());
+        }
+    }
+    return status;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 283a5f0..302e3e1 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -18,8 +18,6 @@
 
 #include <aidl/android/hardware/vibrator/IVibrator.h>
 #include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <hardware/vibrator.h>
 
 #include <utils/Log.h>
 
@@ -31,15 +29,10 @@
 using aidl::android::hardware::vibrator::CompositePrimitive;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
 
 using std::chrono::milliseconds;
 
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
 namespace android {
 
 namespace vibrator {
@@ -53,9 +46,9 @@
         return nullptr;
     }
 
-    auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default";
+    auto serviceName = std::string(IVibrator::descriptor) + "/default";
     if (AServiceManager_isDeclared(serviceName.c_str())) {
-        std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder(
+        std::shared_ptr<IVibrator> hal = IVibrator::fromBinder(
                 ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
         if (hal) {
             ALOGV("Successfully connected to Vibrator HAL AIDL service.");
@@ -63,30 +56,9 @@
         }
     }
 
-    sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
-    if (halV1_0 == nullptr) {
-        ALOGV("Vibrator HAL service not available.");
-        gHalExists = false;
-        return nullptr;
-    }
-
-    sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
-    if (halV1_3) {
-        ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
-        return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
-    }
-    sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
-    if (halV1_2) {
-        ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
-        return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
-    }
-    sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
-    if (halV1_1) {
-        ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
-        return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
-    }
-    ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
-    return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+    ALOGV("Vibrator HAL service not available.");
+    gHalExists = false;
+    return nullptr;
 }
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 536a6b3..5d4c17d 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "VibratorHalWrapper"
 
 #include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <hardware/vibrator.h>
 #include <cmath>
 
@@ -33,32 +32,18 @@
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
 using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
+using aidl::android::hardware::vibrator::IVibrator;
 using aidl::android::hardware::vibrator::PrimitivePwle;
 using aidl::android::hardware::vibrator::VendorEffect;
 
 using std::chrono::milliseconds;
 
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
 namespace android {
 
 namespace vibrator {
 
 // -------------------------------------------------------------------------------------------------
 
-template <class T>
-bool isStaticCastValid(Effect effect) {
-    T castEffect = static_cast<T>(effect);
-    auto iter = hardware::hidl_enum_range<T>();
-    return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
-}
-
-// -------------------------------------------------------------------------------------------------
-
 Info HalWrapper::getInfo() {
     getCapabilities();
     getPrimitiveDurations();
@@ -261,7 +246,7 @@
     if (!result.isOk()) {
         return;
     }
-    std::shared_ptr<Aidl::IVibrator> newHandle = result.value();
+    std::shared_ptr<IVibrator> newHandle = result.value();
     if (newHandle) {
         std::lock_guard<std::mutex> lock(mHandleMutex);
         mHandle = std::move(newHandle);
@@ -514,219 +499,13 @@
                                                         frequencyToOutputAccelerationMap);
 }
 
-std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
+std::shared_ptr<IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
 }
 
 // -------------------------------------------------------------------------------------------------
 
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::ping() {
-    return HalResultFactory::fromReturn(getHal()->ping());
-}
-
-template <typename I>
-void HidlHalWrapper<I>::tryReconnect() {
-    sp<I> newHandle = I::tryGetService();
-    if (newHandle) {
-        std::lock_guard<std::mutex> lock(mHandleMutex);
-        mHandle = std::move(newHandle);
-    }
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
-                                      const std::function<void()>& completionCallback) {
-    auto status = getHal()->on(timeout.count());
-    auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-    if (ret.isOk()) {
-        mCallbackScheduler->schedule(completionCallback, timeout);
-    }
-    return ret;
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::off() {
-    auto status = getHal()->off();
-    return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
-    uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
-    auto status = getHal()->setAmplitude(amp);
-    return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
-    ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
-    return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
-    ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
-    return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
-    ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
-    return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
-    hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
-    Capabilities capabilities =
-            result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
-    return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities);
-}
-
-template <typename I>
-template <typename T>
-HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
-        perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
-        const std::function<void()>& completionCallback) {
-    V1_0::Status status;
-    int32_t lengthMs;
-    auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
-        status = retStatus;
-        lengthMs = retLengthMs;
-    };
-
-    V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
-    auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
-    milliseconds length = milliseconds(lengthMs);
-
-    auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length);
-    if (ret.isOk()) {
-        mCallbackScheduler->schedule(completionCallback, length);
-    }
-
-    return ret;
-}
-
-template <typename I>
-sp<I> HidlHalWrapper<I>::getHal() {
-    std::lock_guard<std::mutex> lock(mHandleMutex);
-    return mHandle;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    if (isStaticCastValid<V1_0::Effect>(effect)) {
-        return performInternal(&V1_0::IVibrator::perform, getHal(),
-                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
-    }
-
-    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
-          Aidl::toString(effect).c_str());
-    return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    if (isStaticCastValid<V1_0::Effect>(effect)) {
-        return performInternal(&V1_1::IVibrator::perform, getHal(),
-                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
-                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
-    }
-
-    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
-          Aidl::toString(effect).c_str());
-    return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    if (isStaticCastValid<V1_0::Effect>(effect)) {
-        return performInternal(&V1_2::IVibrator::perform, getHal(),
-                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
-                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_2::Effect>(effect)) {
-        return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
-                               static_cast<V1_2::Effect>(effect), strength, completionCallback);
-    }
-
-    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
-          Aidl::toString(effect).c_str());
-    return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
-    auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
-    return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    if (isStaticCastValid<V1_0::Effect>(effect)) {
-        return performInternal(&V1_3::IVibrator::perform, getHal(),
-                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
-                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_2::Effect>(effect)) {
-        return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
-                               static_cast<V1_2::Effect>(effect), strength, completionCallback);
-    }
-    if (isStaticCastValid<V1_3::Effect>(effect)) {
-        return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
-                               static_cast<V1_3::Effect>(effect), strength, completionCallback);
-    }
-
-    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
-          Aidl::toString(effect).c_str());
-    return HalResult<milliseconds>::unsupported();
-}
-
-HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
-    Capabilities capabilities = Capabilities::NONE;
-
-    sp<V1_3::IVibrator> hal = getHal();
-    auto amplitudeResult = hal->supportsAmplitudeControl();
-    if (!amplitudeResult.isOk()) {
-        return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities);
-    }
-
-    auto externalControlResult = hal->supportsExternalControl();
-    if (amplitudeResult.withDefault(false)) {
-        capabilities |= Capabilities::AMPLITUDE_CONTROL;
-    }
-    if (externalControlResult.withDefault(false)) {
-        capabilities |= Capabilities::EXTERNAL_CONTROL;
-
-        if (amplitudeResult.withDefault(false)) {
-            capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
-        }
-    }
-
-    return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult),
-                                                      capabilities);
-}
-
-// -------------------------------------------------------------------------------------------------
-
 }; // namespace vibrator
 
 }; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 494f88f..31b6ed0 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -20,7 +20,10 @@
 
 #include <vibratorservice/VibratorManagerHalController.h>
 
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
 
 namespace android {
 
@@ -29,9 +32,9 @@
 std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) {
     static bool gHalExists = true;
     if (gHalExists) {
-        auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
+        auto serviceName = std::string(IVibratorManager::descriptor) + "/default";
         if (AServiceManager_isDeclared(serviceName.c_str())) {
-            std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder(
+            std::shared_ptr<IVibratorManager> hal = IVibratorManager::fromBinder(
                     ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
             if (hal) {
                 ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
@@ -41,6 +44,7 @@
         }
     }
 
+    ALOGV("VibratorManager HAL service not available.");
     gHalExists = false;
     return std::make_shared<LegacyManagerHalWrapper>();
 }
@@ -150,10 +154,10 @@
     return apply(cancelSyncedFn, "cancelSynced");
 }
 
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession(
-        const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalController::startSession(
+        const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
         const std::function<void()>& completionCallback) {
-    hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn =
+    hal_fn<std::shared_ptr<IVibrationSession>> startSessionFn =
             [&](std::shared_ptr<ManagerHalWrapper> hal) {
                 return hal->startSession(ids, config, completionCallback);
             };
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 3db8ff8..bab3f58 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -20,7 +20,10 @@
 
 #include <vibratorservice/VibratorManagerHalWrapper.h>
 
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
 
 namespace android {
 
@@ -41,10 +44,9 @@
     return HalResult<void>::unsupported();
 }
 
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession(
-        const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&,
-        const std::function<void()>&) {
-    return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported();
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalWrapper::startSession(
+        const std::vector<int32_t>&, const VibrationSessionConfig&, const std::function<void()>&) {
+    return HalResult<std::shared_ptr<IVibrationSession>>::unsupported();
 }
 
 HalResult<void> ManagerHalWrapper::clearSessions() {
@@ -87,11 +89,11 @@
 
 std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
         int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
-    std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
-        std::shared_ptr<Aidl::IVibrator> vibrator;
+    std::function<HalResult<std::shared_ptr<IVibrator>>()> reconnectFn = [=, this]() {
+        std::shared_ptr<IVibrator> vibrator;
         auto status = this->getHal()->getVibrator(vibratorId, &vibrator);
-        return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status),
-                                                                              vibrator);
+        return HalResultFactory::fromStatus<std::shared_ptr<IVibrator>>(std::move(status),
+                                                                        vibrator);
     };
     auto result = reconnectFn();
     if (!result.isOk()) {
@@ -110,8 +112,8 @@
 }
 
 void AidlManagerHalWrapper::tryReconnect() {
-    auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
-    std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder(
+    auto aidlServiceName = std::string(IVibratorManager::descriptor) + "/default";
+    std::shared_ptr<IVibratorManager> newHandle = IVibratorManager::fromBinder(
             ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str())));
     if (newHandle) {
         std::lock_guard<std::mutex> lock(mHandleMutex);
@@ -198,15 +200,14 @@
     return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
 }
 
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession(
-        const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> AidlManagerHalWrapper::startSession(
+        const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
         const std::function<void()>& completionCallback) {
     auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
-    std::shared_ptr<Aidl::IVibrationSession> session;
+    std::shared_ptr<IVibrationSession> session;
     auto status = getHal()->startSession(ids, config, cb, &session);
-    return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status),
-                                                                                  std::move(
-                                                                                          session));
+    return HalResultFactory::fromStatus<std::shared_ptr<IVibrationSession>>(std::move(status),
+                                                                            std::move(session));
 }
 
 HalResult<void> AidlManagerHalWrapper::cancelSynced() {
@@ -227,7 +228,7 @@
     return HalResultFactory::fromStatus(getHal()->clearSessions());
 }
 
-std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
+std::shared_ptr<IVibratorManager> AidlManagerHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
 }
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 915d6c7..6fc5cf3 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -29,15 +29,10 @@
     ],
     shared_libs: [
         "libbinder_ndk",
-        "libhidlbase",
         "liblog",
         "libutils",
         "libvibratorservice",
         "android.hardware.vibrator-V3-ndk",
-        "android.hardware.vibrator@1.0",
-        "android.hardware.vibrator@1.1",
-        "android.hardware.vibrator@1.2",
-        "android.hardware.vibrator@1.3",
     ],
     cflags: [
         "-Wall",
diff --git a/services/vibratorservice/include/vibratorservice/VibratorController.h b/services/vibratorservice/include/vibratorservice/VibratorController.h
new file mode 100644
index 0000000..691c8ae
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorController.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_CONTROLLER_H
+#define ANDROID_OS_VIBRATOR_CONTROLLER_H
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+/* Provider for IVibrator HAL service instances. */
+class VibratorProvider {
+public:
+    using IVibrator = ::aidl::android::hardware::vibrator::IVibrator;
+
+    VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {}
+    virtual ~VibratorProvider() = default;
+
+    /* Returns true if vibrator HAL service is declared in the device, false otherwise. */
+    virtual bool isDeclared();
+
+    /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */
+    virtual std::shared_ptr<IVibrator> waitForVibrator();
+
+    /* Connects to vibrator HAL if declared and available, without waiting. */
+    virtual std::shared_ptr<IVibrator> checkForVibrator();
+
+private:
+    std::mutex mMutex;
+    const std::string mServiceName;
+    std::optional<bool> mIsDeclared GUARDED_BY(mMutex);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/* Controller for Vibrator HAL handle.
+ * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects
+ * after each transaction failed call. This also ensures connecting to the service is thread-safe.
+ */
+class VibratorController {
+public:
+    using Effect = ::aidl::android::hardware::vibrator::Effect;
+    using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength;
+    using IVibrator = ::aidl::android::hardware::vibrator::IVibrator;
+    using Status = ::ndk::ScopedAStatus;
+    using VibratorOp = std::function<Status(IVibrator*)>;
+
+    VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {}
+    VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider)
+          : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {}
+    virtual ~VibratorController() = default;
+
+    /* Connects HAL service, possibly waiting for the declared service to become available.
+     * This will automatically be called at the first API usage if it was not manually called
+     * beforehand. Call this manually during the setup phase to avoid slowing the first API call.
+     * Returns true if HAL service is declared, false otherwise.
+     */
+    bool init();
+
+    /* Turn vibrator off. */
+    Status off();
+
+    /* Set vibration amplitude in [0,1]. */
+    Status setAmplitude(float amplitude);
+
+    /* Enable/disable external control. */
+    Status setExternalControl(bool enabled);
+
+    /* Enable always-on for given id, with given effect and strength. */
+    Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength);
+
+    /* Disable always-on for given id. */
+    Status alwaysOnDisable(int32_t id);
+
+private:
+    /* Max number of attempts to perform an operation when it fails with transaction error. */
+    static constexpr int MAX_ATTEMPTS = 2;
+
+    std::mutex mMutex;
+    std::shared_ptr<VibratorProvider> mVibratorProvider;
+    std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex);
+
+    /* Reconnects HAL service without waiting for the service to become available. */
+    std::shared_ptr<IVibrator> reconnectToVibrator();
+
+    /* Perform given operation on HAL with retries on transaction failures. */
+    Status doWithRetries(const VibratorOp& op, const char* logLabel);
+
+    /* Perform given operation on HAL with logs for error/unsupported results. */
+    static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_CONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index a1cb3fa..9b3202b 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed
+
 #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
 #define ANDROID_OS_VIBRATORHALCONTROLLER_H
 
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 9a39ad4..68568d4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed
+
 #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
 #define ANDROID_OS_VIBRATORHALWRAPPER_H
 
@@ -22,7 +24,6 @@
 
 #include <android-base/thread_annotations.h>
 #include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <binder/IServiceManager.h>
 
 #include <vibratorservice/VibratorCallbackScheduler.h>
@@ -105,26 +106,6 @@
                              : fromFailedStatus<T>(std::move(status));
     }
 
-    template <typename T>
-    static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status&& status, T data) {
-        return (status == hardware::vibrator::V1_0::Status::OK)
-                ? HalResult<T>::ok(std::move(data))
-                : fromFailedStatus<T>(std::move(status));
-    }
-
-    template <typename T, typename R>
-    static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) {
-        return ret.isOk() ? HalResult<T>::ok(std::move(data))
-                          : fromFailedReturn<T, R>(std::move(ret));
-    }
-
-    template <typename T, typename R>
-    static HalResult<T> fromReturn(hardware::Return<R>&& ret,
-                                   hardware::vibrator::V1_0::Status status, T data) {
-        return ret.isOk() ? fromStatus<T>(std::move(status), std::move(data))
-                          : fromFailedReturn<T, R>(std::move(ret));
-    }
-
     static HalResult<void> fromStatus(status_t status) {
         return (status == android::OK) ? HalResult<void>::ok()
                                        : fromFailedStatus<void>(std::move(status));
@@ -134,17 +115,6 @@
         return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status));
     }
 
-    static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) {
-        return (status == hardware::vibrator::V1_0::Status::OK)
-                ? HalResult<void>::ok()
-                : fromFailedStatus<void>(std::move(status));
-    }
-
-    template <typename R>
-    static HalResult<void> fromReturn(hardware::Return<R>&& ret) {
-        return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret));
-    }
-
 private:
     template <typename T>
     static HalResult<T> fromFailedStatus(status_t status) {
@@ -166,23 +136,6 @@
         }
         return HalResult<T>::failed(status.getMessage());
     }
-
-    template <typename T>
-    static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status&& status) {
-        switch (status) {
-            case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION:
-                return HalResult<T>::unsupported();
-            default:
-                auto msg = "android::hardware::vibrator::V1_0::Status = " + toString(status);
-                return HalResult<T>::failed(msg.c_str());
-        }
-    }
-
-    template <typename T, typename R>
-    static HalResult<T> fromFailedReturn(hardware::Return<R>&& ret) {
-        return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str())
-                                  : HalResult<T>::failed(ret.description().c_str());
-    }
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -548,108 +501,6 @@
     std::shared_ptr<IVibrator> getHal();
 };
 
-// Wrapper for the HDIL Vibrator HALs.
-template <typename I>
-class HidlHalWrapper : public HalWrapper {
-public:
-    HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
-          : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
-    virtual ~HidlHalWrapper() = default;
-
-    HalResult<void> ping() override final;
-    void tryReconnect() override final;
-
-    HalResult<void> on(std::chrono::milliseconds timeout,
-                       const std::function<void()>& completionCallback) override final;
-    HalResult<void> off() override final;
-
-    HalResult<void> setAmplitude(float amplitude) override final;
-    virtual HalResult<void> setExternalControl(bool enabled) override;
-
-    HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect,
-                                   HalWrapper::EffectStrength strength) override final;
-    HalResult<void> alwaysOnDisable(int32_t id) override final;
-
-protected:
-    std::mutex mHandleMutex;
-    sp<I> mHandle GUARDED_BY(mHandleMutex);
-
-    virtual HalResult<Capabilities> getCapabilitiesInternal() override;
-
-    template <class T>
-    using perform_fn =
-            hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
-                                          hardware::vibrator::V1_0::IVibrator::perform_cb);
-
-    template <class T>
-    HalResult<std::chrono::milliseconds> performInternal(
-            perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength,
-            const std::function<void()>& completionCallback);
-
-    sp<I> getHal();
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.0.
-class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
-public:
-    HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
-                       sp<hardware::vibrator::V1_0::IVibrator> handle)
-          : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
-                                                                std::move(handle)) {}
-    virtual ~HidlHalWrapperV1_0() = default;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
-            const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.1.
-class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
-public:
-    HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
-                       sp<hardware::vibrator::V1_1::IVibrator> handle)
-          : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
-                                                                std::move(handle)) {}
-    virtual ~HidlHalWrapperV1_1() = default;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
-            const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.2.
-class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
-public:
-    HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
-                       sp<hardware::vibrator::V1_2::IVibrator> handle)
-          : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
-                                                                std::move(handle)) {}
-    virtual ~HidlHalWrapperV1_2() = default;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
-            const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.3.
-class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
-public:
-    HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
-                       sp<hardware::vibrator::V1_3::IVibrator> handle)
-          : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
-                                                                std::move(handle)) {}
-    virtual ~HidlHalWrapperV1_3() = default;
-
-    HalResult<void> setExternalControl(bool enabled) override final;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
-            const std::function<void()>& completionCallback) override final;
-
-protected:
-    HalResult<Capabilities> getCapabilitiesInternal() override final;
-};
-
 // -------------------------------------------------------------------------------------------------
 
 }; // namespace vibrator
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 92527eb..92c6286 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -27,12 +27,9 @@
     test_suites: ["device-tests"],
     srcs: [
         "VibratorCallbackSchedulerTest.cpp",
+        "VibratorControllerTest.cpp",
         "VibratorHalControllerTest.cpp",
         "VibratorHalWrapperAidlTest.cpp",
-        "VibratorHalWrapperHidlV1_0Test.cpp",
-        "VibratorHalWrapperHidlV1_1Test.cpp",
-        "VibratorHalWrapperHidlV1_2Test.cpp",
-        "VibratorHalWrapperHidlV1_3Test.cpp",
         "VibratorManagerHalControllerTest.cpp",
         "VibratorManagerHalWrapperAidlTest.cpp",
         "VibratorManagerHalWrapperLegacyTest.cpp",
@@ -43,17 +40,12 @@
         "-Wextra",
     ],
     shared_libs: [
+        "android.hardware.vibrator-V3-ndk",
         "libbase",
         "libbinder_ndk",
-        "libhidlbase",
         "liblog",
-        "libvibratorservice",
         "libutils",
-        "android.hardware.vibrator-V3-ndk",
-        "android.hardware.vibrator@1.0",
-        "android.hardware.vibrator@1.1",
-        "android.hardware.vibrator@1.2",
-        "android.hardware.vibrator@1.3",
+        "libvibratorservice",
     ],
     static_libs: [
         "libgmock",
diff --git a/services/vibratorservice/test/VibratorControllerTest.cpp b/services/vibratorservice/test/VibratorControllerTest.cpp
new file mode 100644
index 0000000..11ec75b
--- /dev/null
+++ b/services/vibratorservice/test/VibratorControllerTest.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2025 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 "VibratorControllerTest"
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorController.h>
+
+#include "test_mocks.h"
+#include "test_utils.h"
+
+using ::aidl::android::hardware::vibrator::Effect;
+using ::aidl::android::hardware::vibrator::EffectStrength;
+using ::aidl::android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace testing;
+
+const auto kReturnOk = []() { return ndk::ScopedAStatus::ok(); };
+const auto kReturnUnsupported = []() {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+};
+const auto kReturnTransactionFailed = []() {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+};
+const auto kReturnUnknownTransaction = []() {
+    return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+};
+const auto kReturnIllegalArgument = []() {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/* Provides mock IVibrator instance for testing. */
+class FakeVibratorProvider : public vibrator::VibratorProvider {
+public:
+    FakeVibratorProvider()
+          : mIsDeclared(true),
+            mMockVibrator(ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>()),
+            mConnectCount(0),
+            mReconnectCount(0) {}
+    virtual ~FakeVibratorProvider() = default;
+
+    bool isDeclared() override { return mIsDeclared; }
+
+    std::shared_ptr<IVibrator> waitForVibrator() override {
+        mConnectCount++;
+        return mIsDeclared ? mMockVibrator : nullptr;
+    }
+
+    std::shared_ptr<IVibrator> checkForVibrator() override {
+        mReconnectCount++;
+        return mIsDeclared ? mMockVibrator : nullptr;
+    }
+
+    void setDeclared(bool isDeclared) { mIsDeclared = isDeclared; }
+
+    int32_t getConnectCount() { return mConnectCount; }
+
+    int32_t getReconnectCount() { return mReconnectCount; }
+
+    std::shared_ptr<StrictMock<vibrator::MockIVibrator>> getMockVibrator() { return mMockVibrator; }
+
+private:
+    bool mIsDeclared;
+    std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator;
+    int32_t mConnectCount;
+    int32_t mReconnectCount;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorControllerTest : public Test {
+public:
+    void SetUp() override {
+        mProvider = std::make_shared<FakeVibratorProvider>();
+        mController = std::make_unique<vibrator::VibratorController>(mProvider);
+        ASSERT_NE(mController, nullptr);
+    }
+
+protected:
+    std::shared_ptr<FakeVibratorProvider> mProvider = nullptr;
+    std::unique_ptr<vibrator::VibratorController> mController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorControllerTest, TestInitServiceDeclared) {
+    ASSERT_TRUE(mController->init());
+    ASSERT_EQ(1, mProvider->getConnectCount());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+
+    // Noop when wrapper was already initialized.
+    ASSERT_TRUE(mController->init());
+    ASSERT_EQ(1, mProvider->getConnectCount());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestInitServiceNotDeclared) {
+    mProvider->setDeclared(false);
+
+    ASSERT_FALSE(mController->init());
+    ASSERT_EQ(0, mProvider->getConnectCount());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+
+    ASSERT_FALSE(mController->init());
+    ASSERT_EQ(0, mProvider->getConnectCount());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestFirstCallTriggersInit) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+
+    auto status = mController->off();
+    ASSERT_TRUE(status.isOk());
+    ASSERT_EQ(1, mProvider->getConnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestSuccessfulResultDoesNotRetry) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+
+    auto status = mController->off();
+    ASSERT_TRUE(status.isOk());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestUnsupportedOperationResultDoesNotRetry) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnUnsupported);
+
+    auto status = mController->off();
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestUnknownTransactionResultDoesNotRetry) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnUnknownTransaction);
+
+    auto status = mController->off();
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestOperationFailedDoesNotRetry) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnIllegalArgument);
+
+    auto status = mController->off();
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestTransactionFailedRetriesOnlyOnce) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(2))
+            .WillRepeatedly(kReturnTransactionFailed);
+
+    auto status = mController->off();
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(1, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestTransactionFailedThenSucceedsReturnsSuccessAfterRetries) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(2))
+            .WillOnce(kReturnTransactionFailed)
+            .WillRepeatedly(kReturnOk);
+
+    auto status = mController->off();
+    ASSERT_TRUE(status.isOk());
+    ASSERT_EQ(1, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestOff) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+
+    auto status = mController->off();
+    ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VibratorControllerTest, TestSetAmplitude) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.1f)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.2f)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnIllegalArgument);
+
+    ASSERT_TRUE(mController->setAmplitude(0.1f).isOk());
+    ASSERT_FALSE(mController->setAmplitude(0.2f).isOk());
+}
+
+TEST_F(VibratorControllerTest, TestSetExternalControl) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(false)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+    EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(true)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnIllegalArgument);
+
+    ASSERT_TRUE(mController->setExternalControl(false).isOk());
+    ASSERT_FALSE(mController->setExternalControl(true).isOk());
+}
+
+TEST_F(VibratorControllerTest, TestAlwaysOnEnable) {
+    EXPECT_CALL(*mProvider->getMockVibrator().get(),
+                alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnOk);
+    EXPECT_CALL(*mProvider->getMockVibrator().get(),
+                alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+            .Times(Exactly(1))
+            .WillRepeatedly(kReturnIllegalArgument);
+
+    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+    ASSERT_FALSE(mController->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM).isOk());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
deleted file mode 100644
index 04dbe4e..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ /dev/null
@@ -1,398 +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.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_0Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/persistable_bundle_aidl.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-
-using aidl::android::hardware::vibrator::Braking;
-using aidl::android::hardware::vibrator::CompositeEffect;
-using aidl::android::hardware::vibrator::CompositePrimitive;
-using aidl::android::hardware::vibrator::CompositePwleV2;
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2Primitive;
-using aidl::android::hardware::vibrator::VendorEffect;
-using aidl::android::os::PersistableBundle;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_0 : public V1_0::IVibrator {
-public:
-    MOCK_METHOD(hardware::Return<void>, ping, (), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
-    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
-    MOCK_METHOD(hardware::Return<void>, perform,
-                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_0Test : public Test {
-public:
-    void SetUp() override {
-        mMockHal = new StrictMock<MockIVibratorV1_0>();
-        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
-        ASSERT_NE(mWrapper, nullptr);
-    }
-
-protected:
-    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
-    std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
-    sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
-    EXPECT_CALL(*mMockHal.get(), ping())
-            .Times(Exactly(2))
-            .WillOnce([]() { return hardware::Return<void>(); })
-            .WillRepeatedly([]() {
-                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-            });
-
-    ASSERT_TRUE(mWrapper->ping().isOk());
-    ASSERT_TRUE(mWrapper->ping().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint32_t) {
-                    return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
-                });
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint32_t) {
-                    return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
-                });
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint32_t) {
-                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
-                });
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
-    ASSERT_EQ(1, *callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
-    ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
-    ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
-
-    // Callback not triggered for unsupported and on failure
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
-    EXPECT_CALL(*mMockHal.get(), off())
-            .Times(Exactly(4))
-            .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
-            .WillOnce([]() {
-                return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
-            })
-            .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
-            .WillRepeatedly([]() {
-                return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
-            });
-
-    ASSERT_TRUE(mWrapper->off().isOk());
-    ASSERT_TRUE(mWrapper->off().isUnsupported());
-    ASSERT_TRUE(mWrapper->off().isFailed());
-    ASSERT_TRUE(mWrapper->off().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1))))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint8_t) {
-                    return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
-                });
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint8_t) {
-                    return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
-                });
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
-                .Times(Exactly(1))
-                .WillRepeatedly([](uint8_t) {
-                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
-                });
-    }
-
-    auto maxAmplitude = std::numeric_limits<uint8_t>::max();
-    ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk());
-    ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported());
-    ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed());
-    ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
-    ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
-    ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
-    ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
-    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-            .Times(Exactly(2))
-            .WillOnce([]() {
-                return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-            })
-            .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-
-    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
-    vibrator::Info info = mWrapper->getInfo();
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
-    ASSERT_TRUE(info.supportedEffects.isUnsupported());
-    ASSERT_TRUE(info.supportedBraking.isUnsupported());
-    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
-    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
-    ASSERT_TRUE(info.primitiveDelayMax.isUnsupported());
-    ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported());
-    ASSERT_TRUE(info.compositionSizeMax.isUnsupported());
-    ASSERT_TRUE(info.pwleSizeMax.isUnsupported());
-    ASSERT_TRUE(info.minFrequency.isUnsupported());
-    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
-    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
-    ASSERT_TRUE(info.qFactor.isUnsupported());
-    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
-    ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
-    ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
-    ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
-    ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
-    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
-        return hardware::Return<bool>(false);
-    });
-
-    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
-    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
-        return hardware::Return<bool>(true);
-    });
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(
-                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    vibrator::Info info = mWrapper->getInfo();
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
-    ASSERT_TRUE(info.supportedEffects.isUnsupported());
-    ASSERT_TRUE(info.supportedBraking.isUnsupported());
-    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
-    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
-    ASSERT_TRUE(info.minFrequency.isUnsupported());
-    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
-    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
-    ASSERT_TRUE(info.qFactor.isUnsupported());
-    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
-    ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
-    ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
-    ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
-    ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
-                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
-                .Times(Exactly(2))
-                .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
-                    cb(V1_0::Status::BAD_VALUE, 10);
-                    return hardware::Return<void>();
-                })
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
-                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-                        });
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-
-    result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
-    ASSERT_TRUE(result.isUnsupported());
-
-    result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    // Callback not triggered for unsupported and on failure
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    // Using TICK that is only available in v1.1
-    auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isUnsupported());
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) {
-    PersistableBundle vendorData; // empty
-    VendorEffect vendorEffect;
-    vendorEffect.vendorData = vendorData;
-    vendorEffect.strength = EffectStrength::LIGHT;
-    vendorEffect.scale = 1.0f;
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported());
-
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
-    std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
-    singleEffect.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
-    multipleEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
-    multipleEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
-    ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
-
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
-    std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
-    multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
-    multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
-    ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
-
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) {
-    CompositePwleV2 composite;
-    composite.pwlePrimitives = {
-            PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
-            PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
-            PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
-    };
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->composePwleV2(composite, callback).isUnsupported());
-
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
deleted file mode 100644
index b0a6537..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
+++ /dev/null
@@ -1,166 +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.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_1Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_1 : public V1_1::IVibrator {
-public:
-    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
-    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
-    MOCK_METHOD(hardware::Return<void>, perform,
-                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_1,
-                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
-                (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_1Test : public Test {
-public:
-    void SetUp() override {
-        mMockHal = new StrictMock<MockIVibratorV1_1>();
-        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
-        ASSERT_NE(mWrapper, nullptr);
-    }
-
-protected:
-    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
-    std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
-    sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
-                                   MockIVibratorV1_1::perform_cb cb) {
-                    cb(V1_0::Status::OK, 10);
-                    return hardware::Return<void>();
-                });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
-                .Times(Exactly(1))
-                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
-                                   MockIVibratorV1_1::perform_cb cb) {
-                    cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
-                    return hardware::Return<void>();
-                });
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
-                .Times(Exactly(2))
-                .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
-                             MockIVibratorV1_1::perform_cb cb) {
-                    cb(V1_0::Status::BAD_VALUE, 0);
-                    return hardware::Return<void>();
-                })
-                .WillRepeatedly(
-                        [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
-                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-                        });
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-
-    result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
-    ASSERT_TRUE(result.isUnsupported());
-
-    result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    // Callback not triggered for unsupported and on failure
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    // Using THUD that is only available in v1.2
-    auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isUnsupported());
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
deleted file mode 100644
index dfe3fa0..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
+++ /dev/null
@@ -1,193 +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.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_2Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_2 : public V1_2::IVibrator {
-public:
-    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
-    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
-    MOCK_METHOD(hardware::Return<void>, perform,
-                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_1,
-                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
-                (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_2,
-                (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_2Test : public Test {
-public:
-    void SetUp() override {
-        mMockHal = new StrictMock<MockIVibratorV1_2>();
-        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
-        ASSERT_NE(mWrapper, nullptr);
-    }
-
-protected:
-    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
-    std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
-    sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
-                                   MockIVibratorV1_2::perform_cb cb) {
-                    cb(V1_0::Status::OK, 10);
-                    return hardware::Return<void>();
-                });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
-                .Times(Exactly(2))
-                .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                    cb(V1_0::Status::BAD_VALUE, 10);
-                    return hardware::Return<void>();
-                })
-                .WillRepeatedly(
-                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
-                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-                        });
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-
-    result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
-    ASSERT_TRUE(result.isUnsupported());
-
-    result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    // Callback not triggered for unsupported and on failure
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    // Using TEXTURE_TICK that is only available in v1.3
-    auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isUnsupported());
-    // No callback is triggered.
-    ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
deleted file mode 100644
index 8624332..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ /dev/null
@@ -1,381 +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.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_3Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_3 : public V1_3::IVibrator {
-public:
-    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
-    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
-    MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
-    MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
-    MOCK_METHOD(hardware::Return<void>, perform,
-                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_1,
-                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
-                (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_2,
-                (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-    MOCK_METHOD(hardware::Return<void>, perform_1_3,
-                (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_3Test : public Test {
-public:
-    void SetUp() override {
-        mMockHal = new StrictMock<MockIVibratorV1_3>();
-        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
-        ASSERT_NE(mWrapper, nullptr);
-    }
-
-protected:
-    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
-    std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
-    sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
-                .Times(Exactly(2))
-                .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
-                .WillRepeatedly([]() {
-                    return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
-                });
-        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
-                .Times(Exactly(2))
-                .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
-                .WillRepeatedly([]() {
-                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
-                });
-    }
-
-    ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
-    ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
-    ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(true);
-        });
-    }
-
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
-                      vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
-              mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(true);
-        });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(false);
-        });
-    }
-
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(false);
-        });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(true);
-        });
-    }
-
-    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(false); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(false);
-        });
-    }
-
-    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() {
-                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-                });
-
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() {
-                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-                });
-    }
-
-    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(false);
-        });
-    }
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(
-                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() {
-                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-                });
-
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() {
-                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-                });
-
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
-                .Times(Exactly(1))
-                .WillRepeatedly([]() { return hardware::Return<bool>(false); });
-    }
-
-    // Call to supportsAmplitudeControl failed.
-    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
-    // Call to supportsExternalControl failed.
-    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
-    // Returns successful result from third call.
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-
-    // Returns cached successful result.
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
-                                   MockIVibratorV1_3::perform_cb cb) {
-                    cb(V1_0::Status::OK, 10);
-                    return hardware::Return<void>();
-                });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-    auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
-
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                            cb(V1_0::Status::OK, 10);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
-                .Times(Exactly(1))
-                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
-                                _))
-                .Times(Exactly(1))
-                .WillRepeatedly(
-                        [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
-                            return hardware::Return<void>();
-                        });
-        EXPECT_CALL(*mMockHal.get(),
-                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
-                                _))
-                .Times(Exactly(2))
-                .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                    cb(V1_0::Status::BAD_VALUE, 0);
-                    return hardware::Return<void>();
-                })
-                .WillRepeatedly(
-                        [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
-                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-                        });
-    }
-
-    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
-    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
-    auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(10ms, result.value());
-    ASSERT_EQ(1, *callbackCounter.get());
-
-    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
-    ASSERT_TRUE(result.isUnsupported());
-
-    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
-    ASSERT_TRUE(result.isFailed());
-
-    // Callback not triggered for unsupported and on failure
-    ASSERT_EQ(1, *callbackCounter.get());
-}
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 879d2d0..d75058a 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -22,6 +22,13 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+// Expose internal header files to test testing binary
+cc_library_headers {
+    name: "libvulkanprivate_headers-testing",
+    export_include_dirs: ["."],
+    visibility: ["//frameworks/native/vulkan/tests"],
+}
+
 ndk_library {
     name: "libvulkan",
     symbol_file: "libvulkan.map.txt",
@@ -41,14 +48,9 @@
     aconfig_declarations: "libvulkan_flags",
 }
 
-cc_library_shared {
-    name: "libvulkan",
-    llndk: {
-        symbol_file: "libvulkan.map.txt",
-        export_llndk_headers: [
-            "vulkan_headers",
-        ],
-    },
+cc_defaults {
+    name: "libvulkan_defaults",
+
     sanitize: {
         misc_undefined: ["integer"],
     },
@@ -81,6 +83,34 @@
         "-Wno-global-constructors",
         "-Wno-zero-length-array",
     ],
+}
+
+cc_library {
+    name: "libvulkanallocator",
+    defaults: ["libvulkan_defaults"],
+    cflags: [
+        // This code uses malloc_usable_size(),
+        // and thus can't be built with _FORTIFY_SOURCE=3.
+        "-U_FORTIFY_SOURCE",
+        "-D_FORTIFY_SOURCE=2",
+    ],
+    srcs: [
+        "allocator.cpp",
+    ],
+    header_libs: [
+        "vulkan_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "libvulkan",
+    defaults: ["libvulkan_defaults"],
+    llndk: {
+        symbol_file: "libvulkan.map.txt",
+        export_llndk_headers: [
+            "vulkan_headers",
+        ],
+    },
 
     srcs: [
         "api.cpp",
@@ -124,6 +154,7 @@
     ],
     static_libs: [
         "libgrallocusage",
+        "libvulkanallocator",
         "libvulkanflags",
     ],
 }
diff --git a/vulkan/libvulkan/TEST_MAPPING b/vulkan/libvulkan/TEST_MAPPING
new file mode 100644
index 0000000..16e342b7
--- /dev/null
+++ b/vulkan/libvulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libvulkan_test"
+    }
+  ]
+}
diff --git a/vulkan/libvulkan/allocator.cpp b/vulkan/libvulkan/allocator.cpp
new file mode 100644
index 0000000..2ca0586
--- /dev/null
+++ b/vulkan/libvulkan/allocator.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2025 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "allocator.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include <log/log.h>
+
+// #define ENABLE_ALLOC_CALLSTACKS 1
+#if ENABLE_ALLOC_CALLSTACKS
+#include <utils/CallStack.h>
+#define ALOGD_CALLSTACK(...)                             \
+    do {                                                 \
+        ALOGD(__VA_ARGS__);                              \
+        android::CallStack callstack;                    \
+        callstack.update();                              \
+        callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, "  "); \
+    } while (false)
+#else
+#define ALOGD_CALLSTACK(...) \
+    do {                     \
+    } while (false)
+#endif
+
+namespace vulkan {
+namespace driver {
+
+namespace {
+
+VKAPI_ATTR void* DefaultAllocate(void*,
+                                 size_t size,
+                                 size_t alignment,
+                                 VkSystemAllocationScope) {
+    void* ptr = nullptr;
+    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
+    // additionally requires that it be at least sizeof(void*).
+    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
+    ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment,
+                    ret, ptr);
+    return ret == 0 ? ptr : nullptr;
+}
+
+// This function is marked `noinline` so that LLVM can't infer an object size
+// for FORTIFY through it, given that it's abusing malloc_usable_size().
+__attribute__((__noinline__))
+VKAPI_ATTR void* DefaultReallocate(void*,
+                                   void* ptr,
+                                   size_t size,
+                                   size_t alignment,
+                                   VkSystemAllocationScope) {
+    if (size == 0) {
+        free(ptr);
+        return nullptr;
+    }
+
+    // TODO(b/143295633): Right now we never shrink allocations; if the new
+    // request is smaller than the existing chunk, we just continue using it.
+    // Right now the loader never reallocs, so this doesn't matter. If that
+    // changes, or if this code is copied into some other project, this should
+    // probably have a heuristic to allocate-copy-free when doing so will save
+    // "enough" space.
+    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
+    if (size <= old_size)
+        return ptr;
+
+    void* new_ptr = nullptr;
+    if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
+        return nullptr;
+    if (ptr) {
+        memcpy(new_ptr, ptr, std::min(old_size, size));
+        free(ptr);
+    }
+    return new_ptr;
+}
+
+VKAPI_ATTR void DefaultFree(void*, void* ptr) {
+    ALOGD_CALLSTACK("Free: %p", ptr);
+    free(ptr);
+}
+
+}  // anonymous namespace
+
+const VkAllocationCallbacks& GetDefaultAllocator() {
+    static const VkAllocationCallbacks kDefaultAllocCallbacks = {
+        .pUserData = nullptr,
+        .pfnAllocation = DefaultAllocate,
+        .pfnReallocation = DefaultReallocate,
+        .pfnFree = DefaultFree,
+    };
+
+    return kDefaultAllocCallbacks;
+}
+
+}  // namespace driver
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/allocator.h b/vulkan/libvulkan/allocator.h
new file mode 100644
index 0000000..9095e92
--- /dev/null
+++ b/vulkan/libvulkan/allocator.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2025 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 <vulkan/vulkan.h>
+
+namespace vulkan {
+namespace driver {
+
+const VkAllocationCallbacks& GetDefaultAllocator();
+
+}  // namespace driver
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7d0f545..28c1b5f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -50,22 +50,6 @@
 
 extern "C" android_namespace_t* android_get_exported_namespace(const char*);
 
-// #define ENABLE_ALLOC_CALLSTACKS 1
-#if ENABLE_ALLOC_CALLSTACKS
-#include <utils/CallStack.h>
-#define ALOGD_CALLSTACK(...)                             \
-    do {                                                 \
-        ALOGD(__VA_ARGS__);                              \
-        android::CallStack callstack;                    \
-        callstack.update();                              \
-        callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, "  "); \
-    } while (false)
-#else
-#define ALOGD_CALLSTACK(...) \
-    do {                     \
-    } while (false)
-#endif
-
 namespace vulkan {
 namespace driver {
 
@@ -829,54 +813,6 @@
     }
 }
 
-VKAPI_ATTR void* DefaultAllocate(void*,
-                                 size_t size,
-                                 size_t alignment,
-                                 VkSystemAllocationScope) {
-    void* ptr = nullptr;
-    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
-    // additionally requires that it be at least sizeof(void*).
-    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
-    ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment,
-                    ret, ptr);
-    return ret == 0 ? ptr : nullptr;
-}
-
-VKAPI_ATTR void* DefaultReallocate(void*,
-                                   void* ptr,
-                                   size_t size,
-                                   size_t alignment,
-                                   VkSystemAllocationScope) {
-    if (size == 0) {
-        free(ptr);
-        return nullptr;
-    }
-
-    // TODO(b/143295633): Right now we never shrink allocations; if the new
-    // request is smaller than the existing chunk, we just continue using it.
-    // Right now the loader never reallocs, so this doesn't matter. If that
-    // changes, or if this code is copied into some other project, this should
-    // probably have a heuristic to allocate-copy-free when doing so will save
-    // "enough" space.
-    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
-    if (size <= old_size)
-        return ptr;
-
-    void* new_ptr = nullptr;
-    if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
-        return nullptr;
-    if (ptr) {
-        memcpy(new_ptr, ptr, std::min(old_size, size));
-        free(ptr);
-    }
-    return new_ptr;
-}
-
-VKAPI_ATTR void DefaultFree(void*, void* ptr) {
-    ALOGD_CALLSTACK("Free: %p", ptr);
-    free(ptr);
-}
-
 InstanceData* AllocateInstanceData(const VkAllocationCallbacks& allocator) {
     void* data_mem = allocator.pfnAllocation(
         allocator.pUserData, sizeof(InstanceData), alignof(InstanceData),
@@ -916,17 +852,6 @@
     return Hal::Open();
 }
 
-const VkAllocationCallbacks& GetDefaultAllocator() {
-    static const VkAllocationCallbacks kDefaultAllocCallbacks = {
-        .pUserData = nullptr,
-        .pfnAllocation = DefaultAllocate,
-        .pfnReallocation = DefaultReallocate,
-        .pfnFree = DefaultFree,
-    };
-
-    return kDefaultAllocCallbacks;
-}
-
 PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) {
     const ProcHook* hook = GetProcHook(pName);
     if (!hook)
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 4b855e5..fa85dd5 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -27,6 +27,7 @@
 #include <vulkan/vulkan.h>
 #include <hardware/hwvulkan.h>
 
+#include "allocator.h"
 #include "api_gen.h"
 #include "driver_gen.h"
 #include "debug_report.h"
@@ -102,7 +103,6 @@
 };
 
 bool OpenHAL();
-const VkAllocationCallbacks& GetDefaultAllocator();
 
 void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 09b0a14..5e2b55e 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1413,6 +1413,119 @@
     allocator->pfnFree(allocator->pUserData, swapchain);
 }
 
+static VkResult getProducerUsageGPDIFP2(
+    const VkPhysicalDevice& pdev,
+    const VkSwapchainCreateInfoKHR* create_info,
+    const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
+    bool create_protected_swapchain,
+    uint64_t* producer_usage) {
+    // Look through the create_info pNext chain passed to createSwapchainKHR
+    // for an image compression control struct.
+    // if one is found AND the appropriate extensions are enabled, create a
+    // VkImageCompressionControlEXT structure to pass on to
+    // GetPhysicalDeviceImageFormatProperties2
+    void* compression_control_pNext = nullptr;
+    VkImageCompressionControlEXT image_compression = {};
+    const VkSwapchainCreateInfoKHR* create_infos = create_info;
+    while (create_infos->pNext) {
+        create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+            create_infos->pNext);
+        switch (create_infos->sType) {
+            case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                const VkImageCompressionControlEXT* compression_infos =
+                    reinterpret_cast<const VkImageCompressionControlEXT*>(
+                        create_infos);
+                image_compression = *compression_infos;
+                image_compression.pNext = nullptr;
+                compression_control_pNext = &image_compression;
+            } break;
+            default:
+                // Ignore all other info structs
+                break;
+        }
+    }
+
+    // call GetPhysicalDeviceImageFormatProperties2KHR
+    VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+        .pNext = compression_control_pNext,
+        .handleType =
+            VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+    };
+
+    // AHB does not have an sRGB format so we can't pass it to GPDIFP
+    // We need to convert the format to unorm if it is srgb
+    VkFormat format = create_info->imageFormat;
+    if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+        format = VK_FORMAT_R8G8B8A8_UNORM;
+    }
+
+    VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+        .pNext = &external_image_format_info,
+        .format = format,
+        .type = VK_IMAGE_TYPE_2D,
+        .tiling = VK_IMAGE_TILING_OPTIMAL,
+        .usage = create_info->imageUsage,
+        .flags =
+            create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+    };
+
+    // If supporting mutable format swapchain add the mutable format flag
+    if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
+        image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+        image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
+    }
+
+    VkAndroidHardwareBufferUsageANDROID ahb_usage;
+    ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+    ahb_usage.pNext = nullptr;
+
+    VkImageFormatProperties2 image_format_properties;
+    image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+    image_format_properties.pNext = &ahb_usage;
+
+    VkResult result = GetPhysicalDeviceImageFormatProperties2(
+        pdev, &image_format_info, &image_format_properties);
+    if (result != VK_SUCCESS) {
+        ALOGE(
+            "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage "
+            "failed: %d",
+            result);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+    // Determine if USAGE_FRONT_BUFFER is needed.
+    // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when
+    // querying for producer_usage. So androidHardwareBufferUsage will not
+    // contain USAGE_FRONT_BUFFER. We need to manually check for usage here.
+    if (!(swapchain_image_usage &
+          VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) {
+        *producer_usage = ahb_usage.androidHardwareBufferUsage;
+        return VK_SUCCESS;
+    }
+
+    // Check if USAGE_FRONT_BUFFER is supported for this swapchain
+    AHardwareBuffer_Desc ahb_desc = {
+        .width = create_info->imageExtent.width,
+        .height = create_info->imageExtent.height,
+        .layers = create_info->imageArrayLayers,
+        .format = create_info->imageFormat,
+        .usage = ahb_usage.androidHardwareBufferUsage |
+                 AHARDWAREBUFFER_USAGE_FRONT_BUFFER,
+        .stride = 0,  // stride is always ignored when calling isSupported()
+    };
+
+    // If FRONT_BUFFER is not supported in the GPDIFP2 path
+    // then we need to fallback to GetSwapchainGrallocUsageXAndroid
+    if (AHardwareBuffer_isSupported(&ahb_desc)) {
+        *producer_usage = ahb_usage.androidHardwareBufferUsage;
+        *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+        return VK_SUCCESS;
+    }
+
+    return VK_ERROR_FORMAT_NOT_SUPPORTED;
+}
+
 static VkResult getProducerUsage(const VkDevice& device,
                                  const VkSwapchainCreateInfoKHR* create_info,
                                  const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
@@ -1422,106 +1535,16 @@
     const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
     const InstanceData& instance_data = GetData(pdev);
     const InstanceDriverTable& instance_dispatch = instance_data.driver;
+
     if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2 ||
             instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
-        // Look through the create_info pNext chain passed to createSwapchainKHR
-        // for an image compression control struct.
-        // if one is found AND the appropriate extensions are enabled, create a
-        // VkImageCompressionControlEXT structure to pass on to
-        // GetPhysicalDeviceImageFormatProperties2
-        void* compression_control_pNext = nullptr;
-        VkImageCompressionControlEXT image_compression = {};
-        const VkSwapchainCreateInfoKHR* create_infos = create_info;
-        while (create_infos->pNext) {
-            create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
-            switch (create_infos->sType) {
-                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
-                    const VkImageCompressionControlEXT* compression_infos =
-                        reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
-                    image_compression = *compression_infos;
-                    image_compression.pNext = nullptr;
-                    compression_control_pNext = &image_compression;
-                } break;
-                default:
-                    // Ignore all other info structs
-                    break;
-            }
-        }
-
-        // call GetPhysicalDeviceImageFormatProperties2KHR
-        VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
-            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
-            .pNext = compression_control_pNext,
-            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
-        };
-
-        // AHB does not have an sRGB format so we can't pass it to GPDIFP
-        // We need to convert the format to unorm if it is srgb
-        VkFormat format = create_info->imageFormat;
-        if (format == VK_FORMAT_R8G8B8A8_SRGB) {
-            format = VK_FORMAT_R8G8B8A8_UNORM;
-        }
-
-        VkPhysicalDeviceImageFormatInfo2 image_format_info = {
-            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
-            .pNext = &external_image_format_info,
-            .format = format,
-            .type = VK_IMAGE_TYPE_2D,
-            .tiling = VK_IMAGE_TILING_OPTIMAL,
-            .usage = create_info->imageUsage,
-            .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
-        };
-
-        // If supporting mutable format swapchain add the mutable format flag
-        if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
-            image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
-            image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
-        }
-
-        VkAndroidHardwareBufferUsageANDROID ahb_usage;
-        ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
-        ahb_usage.pNext = nullptr;
-
-        VkImageFormatProperties2 image_format_properties;
-        image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
-        image_format_properties.pNext = &ahb_usage;
-
-        VkResult result = GetPhysicalDeviceImageFormatProperties2(
-            pdev, &image_format_info, &image_format_properties);
-        if (result != VK_SUCCESS) {
-            ALOGE(
-                "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage "
-                "failed: %d",
-                result);
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-
-        // Determine if USAGE_FRONT_BUFFER is needed.
-        // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when
-        // querying for producer_usage. So androidHardwareBufferUsage will not
-        // contain USAGE_FRONT_BUFFER. We need to manually check for usage here.
-        if (!(swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) {
-            *producer_usage = ahb_usage.androidHardwareBufferUsage;
+        VkResult result =
+            getProducerUsageGPDIFP2(pdev, create_info, swapchain_image_usage,
+                                    create_protected_swapchain, producer_usage);
+        if (result == VK_SUCCESS) {
             return VK_SUCCESS;
         }
-
-        // Check if USAGE_FRONT_BUFFER is supported for this swapchain
-        AHardwareBuffer_Desc ahb_desc = {
-            .width = create_info->imageExtent.width,
-            .height = create_info->imageExtent.height,
-            .layers = create_info->imageArrayLayers,
-            .format = create_info->imageFormat,
-            .usage = ahb_usage.androidHardwareBufferUsage | AHARDWAREBUFFER_USAGE_FRONT_BUFFER,
-            .stride = 0, // stride is always ignored when calling isSupported()
-        };
-
-        // If FRONT_BUFFER is not supported,
-        // then we need to call GetSwapchainGrallocUsageXAndroid below
-        if (AHardwareBuffer_isSupported(&ahb_desc)) {
-            *producer_usage = ahb_usage.androidHardwareBufferUsage;
-            *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
-            return VK_SUCCESS;
-        }
+        // Fall through to gralloc path on error
     }
 
     uint64_t native_usage = 0;
diff --git a/vulkan/scripts/code_generator.py b/vulkan/scripts/code_generator.py
index 2a017d2..051816d 100755
--- a/vulkan/scripts/code_generator.py
+++ b/vulkan/scripts/code_generator.py
@@ -21,6 +21,7 @@
 import driver_generator
 import generator_common
 import null_generator
+import vkjson_generator
 
 if __name__ == '__main__':
   generator_common.parse_vulkan_registry()
@@ -30,3 +31,6 @@
   driver_generator.gen_cpp()
   null_generator.gen_h()
   null_generator.gen_cpp()
+  vkjson_generator.gen_h()
+  vkjson_generator.gen_cc()
+  vkjson_generator.gen_instance_cc()
diff --git a/vulkan/scripts/vk.py b/vulkan/scripts/vk.py
new file mode 100644
index 0000000..983e0dc
--- /dev/null
+++ b/vulkan/scripts/vk.py
@@ -0,0 +1,968 @@
+import ctypes
+import dataclasses
+import enum
+from typing import List
+
+dataclass = dataclasses.dataclass
+Enum = enum.Enum
+
+# TODO(b/401184058): Automate this file for generating the vulkan structs graph from vk.xml
+VK_UUID_SIZE = 16
+VK_LUID_SIZE = 16
+
+VkImageLayout = Enum
+uint8_t = ctypes.c_uint8
+uint32_t = ctypes.c_uint32
+VkFlags = uint32_t
+VkMemoryPropertyFlags = VkFlags
+VkMemoryHeapFlags = VkFlags
+int32_t = int
+uint64_t = ctypes.c_uint64
+VkBool32 = bool
+VkDeviceSize = ctypes.c_uint64
+size_t = int
+VkSampleCountFlags = ctypes.c_uint32
+VkFormatFeatureFlags = ctypes.c_uint32
+VkQueueFlags = ctypes.c_uint32
+VkShaderStageFlags = ctypes.c_uint32
+VkSubgroupFeatureFlags = ctypes.c_uint32
+VkResolveModeFlags = ctypes.c_uint32
+float_t = ctypes.c_float
+VkShaderFloatControlsIndependence = Enum
+VkPointClippingBehavior = Enum
+VkPhysicalDeviceType = Enum
+VkDriverId = Enum
+VkPipelineRobustnessBufferBehavior = Enum
+
+
+@dataclass
+class ConformanceVersion:
+  major: uint8_t
+  minor: uint8_t
+  subminor: uint8_t
+  patch: uint8_t
+
+
+@dataclass
+class VkExtent3D:
+  width: uint32_t
+  height: uint32_t
+  depth: uint32_t
+
+
+@dataclass
+class VkPhysicalDeviceLimits:
+  maxImageDimension1D: uint32_t
+  maxImageDimension2D: uint32_t
+  maxImageDimension3D: uint32_t
+  maxImageDimensionCube: uint32_t
+  maxImageArrayLayers: uint32_t
+  maxTexelBufferElements: uint32_t
+  maxUniformBufferRange: uint32_t
+  maxStorageBufferRange: uint32_t
+  maxPushConstantsSize: uint32_t
+  maxMemoryAllocationCount: uint32_t
+  maxSamplerAllocationCount: uint32_t
+  bufferImageGranularity: VkDeviceSize
+  sparseAddressSpaceSize: VkDeviceSize
+  maxBoundDescriptorSets: uint32_t
+  maxPerStageDescriptorSamplers: uint32_t
+  maxPerStageDescriptorUniformBuffers: uint32_t
+  maxPerStageDescriptorStorageBuffers: uint32_t
+  maxPerStageDescriptorSampledImages: uint32_t
+  maxPerStageDescriptorStorageImages: uint32_t
+  maxPerStageDescriptorInputAttachments: uint32_t
+  maxPerStageResources: uint32_t
+  maxDescriptorSetSamplers: uint32_t
+  maxDescriptorSetUniformBuffers: uint32_t
+  maxDescriptorSetUniformBuffersDynamic: uint32_t
+  maxDescriptorSetStorageBuffers: uint32_t
+  maxDescriptorSetStorageBuffersDynamic: uint32_t
+  maxDescriptorSetSampledImages: uint32_t
+  maxDescriptorSetStorageImages: uint32_t
+  maxDescriptorSetInputAttachments: uint32_t
+  maxVertexInputAttributes: uint32_t
+  maxVertexInputBindings: uint32_t
+  maxVertexInputAttributeOffset: uint32_t
+  maxVertexInputBindingStride: uint32_t
+  maxVertexOutputComponents: uint32_t
+  maxTessellationGenerationLevel: uint32_t
+  maxTessellationPatchSize: uint32_t
+  maxTessellationControlPerVertexInputComponents: uint32_t
+  maxTessellationControlPerVertexOutputComponents: uint32_t
+  maxTessellationControlPerPatchOutputComponents: uint32_t
+  maxTessellationControlTotalOutputComponents: uint32_t
+  maxTessellationEvaluationInputComponents: uint32_t
+  maxTessellationEvaluationOutputComponents: uint32_t
+  maxGeometryShaderInvocations: uint32_t
+  maxGeometryInputComponents: uint32_t
+  maxGeometryOutputComponents: uint32_t
+  maxGeometryOutputVertices: uint32_t
+  maxGeometryTotalOutputComponents: uint32_t
+  maxFragmentInputComponents: uint32_t
+  maxFragmentOutputAttachments: uint32_t
+  maxFragmentDualSrcAttachments: uint32_t
+  maxFragmentCombinedOutputResources: uint32_t
+  maxComputeSharedMemorySize: uint32_t
+  maxComputeWorkGroupCount: uint32_t*3
+  maxComputeWorkGroupInvocations: uint32_t
+  maxComputeWorkGroupSize: uint32_t*3
+  subPixelPrecisionBits: uint32_t
+  subTexelPrecisionBits: uint32_t
+  mipmapPrecisionBits: uint32_t
+  maxDrawIndexedIndexValue: uint32_t
+  maxDrawIndirectCount: uint32_t
+  maxSamplerLodBias: float
+  maxSamplerAnisotropy: float
+  maxViewports: uint32_t
+  maxViewportDimensions: uint32_t*2
+  viewportBoundsRange: float_t*2
+  viewportSubPixelBits: uint32_t
+  minMemoryMapAlignment: size_t
+  minTexelBufferOffsetAlignment: VkDeviceSize
+  minUniformBufferOffsetAlignment: VkDeviceSize
+  minStorageBufferOffsetAlignment: VkDeviceSize
+  minTexelOffset: int32_t
+  maxTexelOffset: uint32_t
+  minTexelGatherOffset: int32_t
+  maxTexelGatherOffset: uint32_t
+  minInterpolationOffset: float
+  maxInterpolationOffset: float
+  subPixelInterpolationOffsetBits: uint32_t
+  maxFramebufferWidth: uint32_t
+  maxFramebufferHeight: uint32_t
+  maxFramebufferLayers: uint32_t
+  framebufferColorSampleCounts: VkSampleCountFlags
+  framebufferDepthSampleCounts: VkSampleCountFlags
+  framebufferStencilSampleCounts: VkSampleCountFlags
+  framebufferNoAttachmentsSampleCounts: VkSampleCountFlags
+  maxColorAttachments: uint32_t
+  sampledImageColorSampleCounts: VkSampleCountFlags
+  sampledImageIntegerSampleCounts: VkSampleCountFlags
+  sampledImageDepthSampleCounts: VkSampleCountFlags
+  sampledImageStencilSampleCounts: VkSampleCountFlags
+  storageImageSampleCounts: VkSampleCountFlags
+  maxSampleMaskWords: uint32_t
+  timestampComputeAndGraphics: VkBool32
+  timestampPeriod: float
+  maxClipDistances: uint32_t
+  maxCullDistances: uint32_t
+  maxCombinedClipAndCullDistances: uint32_t
+  discreteQueuePriorities: uint32_t
+  pointSizeRange: float_t*2
+  lineWidthRange: float_t*2
+  pointSizeGranularity: float
+  lineWidthGranularity: float
+  strictLines: VkBool32
+  standardSampleLocations: VkBool32
+  optimalBufferCopyOffsetAlignment: VkDeviceSize
+  optimalBufferCopyRowPitchAlignment: VkDeviceSize
+  nonCoherentAtomSize: VkDeviceSize
+
+
+@dataclass
+class VkPhysicalDeviceShaderDrawParameterFeatures:
+  shaderDrawParameters: VkBool32
+
+
+@dataclass
+class VkExtensionProperties:
+  extensionName: str
+  specVersion: uint32_t
+
+
+@dataclass
+class VkFormatProperties:
+  linearTilingFeatures: VkFormatFeatureFlags
+  optimalTilingFeatures: VkFormatFeatureFlags
+  bufferFeatures: VkFormatFeatureFlags
+
+
+@dataclass
+class VkLayerProperties:
+  layerName: str
+  specVersion: uint32_t
+  implementationVersion: uint32_t
+  description: str
+
+
+@dataclass
+class VkQueueFamilyProperties:
+  queueFlags: VkQueueFlags
+  queueCount: uint32_t
+  timestampValidBits: uint32_t
+  minImageTransferGranularity: VkExtent3D
+
+
+@dataclass
+class VkPhysicalDeviceSparseProperties:
+  residencyStandard2DBlockShape: VkBool32
+  residencyStandard2DMultisampleBlockShape: VkBool32
+  residencyStandard3DBlockShape: VkBool32
+  residencyAlignedMipSize: VkBool32
+  residencyNonResidentStrict: VkBool32
+
+
+@dataclass
+class VkImageFormatProperties:
+  maxExtent: VkExtent3D
+  maxMipLevels: uint32_t
+  maxArrayLayers: uint32_t
+  sampleCounts: VkSampleCountFlags
+  maxResourceSize: VkDeviceSize
+
+
+@dataclass
+class VkPhysicalDeviceSamplerYcbcrConversionFeatures:
+  samplerYcbcrConversion: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceIDProperties:
+  deviceUUID: uint8_t*VK_UUID_SIZE
+  driverUUID: uint8_t*VK_UUID_SIZE
+  deviceLUID: uint8_t*VK_LUID_SIZE
+  deviceNodeMask: uint32_t
+  deviceLUIDValid: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceMaintenance3Properties:
+  maxPerSetDescriptors: uint32_t
+  maxMemoryAllocationSize: VkDeviceSize
+
+
+@dataclass
+class VkPhysicalDevice16BitStorageFeatures:
+  storageBuffer16BitAccess: VkBool32
+  uniformAndStorageBuffer16BitAccess: VkBool32
+  storagePushConstant16: VkBool32
+  storageInputOutput16: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceMultiviewFeatures:
+  multiview: VkBool32
+  multiviewGeometryShader: VkBool32
+  multiviewTessellationShader: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceSubgroupProperties:
+  subgroupSize: uint32_t
+  supportedStages: VkShaderStageFlags
+  supportedOperations: VkSubgroupFeatureFlags
+  quadOperationsInAllStages: VkBool32
+
+
+@dataclass
+class VkPhysicalDevicePointClippingProperties:
+  pointClippingBehavior: VkPointClippingBehavior
+
+
+@dataclass
+class VkPhysicalDeviceMultiviewProperties:
+  maxMultiviewViewCount: uint32_t
+  maxMultiviewInstanceIndex: uint32_t
+
+
+@dataclass
+class VkMemoryType:
+  propertyFlags: VkMemoryPropertyFlags
+  heapIndex: uint32_t
+
+
+@dataclass
+class VkMemoryHeap:
+  size: VkDeviceSize
+  flags: VkMemoryHeapFlags
+
+
+@dataclass
+class VkPhysicalDeviceMemoryProperties:
+  memoryTypeCount: uint32_t
+  memoryTypes: List[VkMemoryType]
+  memoryHeapCount: uint32_t
+  memoryHeaps: List[VkMemoryHeap]
+
+
+@dataclass
+class VkPhysicalDeviceProperties:
+  apiVersion: uint32_t
+  driverVersion: uint32_t
+  vendorID: uint32_t
+  deviceID: uint32_t
+  deviceType: VkPhysicalDeviceType
+  deviceName: str
+  pipelineCacheUUID: uint8_t
+  limits: VkPhysicalDeviceLimits
+  sparseProperties: VkPhysicalDeviceSparseProperties
+
+
+@dataclass
+class VkPhysicalDeviceFeatures:
+  robustBufferAccess: VkBool32
+  fullDrawIndexUint32: VkBool32
+  imageCubeArray: VkBool32
+  independentBlend: VkBool32
+  geometryShader: VkBool32
+  tessellationShader: VkBool32
+  sampleRateShading: VkBool32
+  dualSrcBlend: VkBool32
+  logicOp: VkBool32
+  multiDrawIndirect: VkBool32
+  drawIndirectFirstInstance: VkBool32
+  depthClamp: VkBool32
+  depthBiasClamp: VkBool32
+  fillModeNonSolid: VkBool32
+  depthBounds: VkBool32
+  wideLines: VkBool32
+  largePoints: VkBool32
+  alphaToOne: VkBool32
+  multiViewport: VkBool32
+  samplerAnisotropy: VkBool32
+  textureCompressionETC2: VkBool32
+  textureCompressionASTC_LDR: VkBool32
+  textureCompressionBC: VkBool32
+  occlusionQueryPrecise: VkBool32
+  pipelineStatisticsQuery: VkBool32
+  vertexPipelineStoresAndAtomics: VkBool32
+  fragmentStoresAndAtomics: VkBool32
+  shaderTessellationAndGeometryPointSize: VkBool32
+  shaderImageGatherExtended: VkBool32
+  shaderStorageImageExtendedFormats: VkBool32
+  shaderStorageImageMultisample: VkBool32
+  shaderStorageImageReadWithoutFormat: VkBool32
+  shaderStorageImageWriteWithoutFormat: VkBool32
+  shaderUniformBufferArrayDynamicIndexing: VkBool32
+  shaderSampledImageArrayDynamicIndexing: VkBool32
+  shaderStorageBufferArrayDynamicIndexing: VkBool32
+  shaderStorageImageArrayDynamicIndexing: VkBool32
+  shaderClipDistance: VkBool32
+  shaderCullDistance: VkBool32
+  shaderFloat64: VkBool32
+  shaderInt64: VkBool32
+  shaderInt16: VkBool32
+  shaderResourceResidency: VkBool32
+  shaderResourceMinLod: VkBool32
+  sparseBinding: VkBool32
+  sparseResidencyBuffer: VkBool32
+  sparseResidencyImage2D: VkBool32
+  sparseResidencyImage3D: VkBool32
+  sparseResidency2Samples: VkBool32
+  sparseResidency4Samples: VkBool32
+  sparseResidency8Samples: VkBool32
+  sparseResidency16Samples: VkBool32
+  sparseResidencyAliased: VkBool32
+  variableMultisampleRate: VkBool32
+  inheritedQueries: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceShaderFloat16Int8Features:
+  shaderFloat16: VkBool32
+  shaderInt8: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceProtectedMemoryFeatures:
+  protectedMemory: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVariablePointersFeatures:
+  variablePointersStorageBuffer: VkBool32
+  variablePointers: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceImage2DViewOf3DFeaturesEXT:
+  image2DViewOf3D: VkBool32
+  sampler2DViewOf3D: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceCustomBorderColorFeaturesEXT:
+  customBorderColors: VkBool32
+  customBorderColorWithoutFormat: VkBool32
+
+
+@dataclass
+class VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT:
+  primitiveTopologyListRestart: VkBool32
+  primitiveTopologyPatchListRestart: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceProvokingVertexFeaturesEXT:
+  provokingVertexLast: VkBool32
+  transformFeedbackPreservesProvokingVertex: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceIndexTypeUint8Features:
+  indexTypeUint8: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVertexAttributeDivisorFeatures:
+  vertexAttributeInstanceRateDivisor: VkBool32
+  vertexAttributeInstanceRateZeroDivisor: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceTransformFeedbackFeaturesEXT:
+  transformFeedback: VkBool32
+  geometryStreams: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR:
+  shaderSubgroupUniformControlFlow: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures:
+  shaderSubgroupExtendedTypes: VkBool32
+
+
+@dataclass
+class VkPhysicalDevice8BitStorageFeatures:
+  storageBuffer8BitAccess: VkBool32
+  uniformAndStorageBuffer8BitAccess: VkBool32
+  storagePushConstant8: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceShaderIntegerDotProductFeatures:
+  shaderIntegerDotProduct: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG:
+  relaxedLineRasterization: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceLineRasterizationFeatures:
+  rectangularLines: VkBool32
+  bresenhamLines: VkBool32
+  smoothLines: VkBool32
+  stippledRectangularLines: VkBool32
+  stippledBresenhamLines: VkBool32
+  stippledSmoothLines: VkBool32
+
+
+@dataclass
+class VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT:
+  primitivesGeneratedQuery: VkBool32
+  primitivesGeneratedQueryWithRasterizerDiscard: VkBool32
+  primitivesGeneratedQueryWithNonZeroStreams: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceFloatControlsProperties:
+  denormBehaviorIndependence : VkShaderFloatControlsIndependence
+  roundingModeIndependence : VkShaderFloatControlsIndependence
+  shaderSignedZeroInfNanPreserveFloat16 : VkBool32
+  shaderSignedZeroInfNanPreserveFloat32 : VkBool32
+  shaderSignedZeroInfNanPreserveFloat64 : VkBool32
+  shaderDenormPreserveFloat16 : VkBool32
+  shaderDenormPreserveFloat32 : VkBool32
+  shaderDenormPreserveFloat64 : VkBool32
+  shaderDenormFlushToZeroFloat16 : VkBool32
+  shaderDenormFlushToZeroFloat32 : VkBool32
+  shaderDenormFlushToZeroFloat64 : VkBool32
+  shaderRoundingModeRTEFloat16 : VkBool32
+  shaderRoundingModeRTEFloat32 : VkBool32
+  shaderRoundingModeRTEFloat64 :VkBool32
+  shaderRoundingModeRTZFloat16 : VkBool32
+  shaderRoundingModeRTZFloat32 : VkBool32
+  shaderRoundingModeRTZFloat64 : VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVulkan11Properties:
+  deviceUUID : uint8_t*VK_UUID_SIZE
+  driverUUID : uint8_t*VK_UUID_SIZE
+  deviceLUID : uint8_t*VK_LUID_SIZE
+  deviceNodeMask : uint32_t
+  deviceLUIDValid : VkBool32
+  subgroupSize : uint32_t
+  subgroupSupportedStages : VkShaderStageFlags
+  subgroupSupportedOperations : VkSubgroupFeatureFlags
+  subgroupQuadOperationsInAllStages : VkBool32
+  pointClippingBehavior : VkPointClippingBehavior
+  maxMultiviewViewCount : uint32_t
+  maxMultiviewInstanceIndex :uint32_t
+  protectedNoFault : VkBool32
+  maxPerSetDescriptors : uint32_t
+  maxMemoryAllocationSize : VkDeviceSize
+
+
+@dataclass
+class VkPhysicalDeviceVulkan11Features:
+  storageBuffer16BitAccess: VkBool32
+  uniformAndStorageBuffer16BitAccess: VkBool32
+  storagePushConstant16: VkBool32
+  storageInputOutput16: VkBool32
+  multiview: VkBool32
+  multiviewGeometryShader: VkBool32
+  multiviewTessellationShader: VkBool32
+  variablePointersStorageBuffer: VkBool32
+  variablePointers: VkBool32
+  protectedMemory: VkBool32
+  samplerYcbcrConversion: VkBool32
+  shaderDrawParameters: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVulkan12Properties:
+  driverID: VkDriverId
+  driverName: str
+  driverInfo: str
+  conformanceVersion: ConformanceVersion
+  denormBehaviorIndependence: VkShaderFloatControlsIndependence
+  roundingModeIndependence: VkShaderFloatControlsIndependence
+  shaderSignedZeroInfNanPreserveFloat16: VkBool32
+  shaderSignedZeroInfNanPreserveFloat32: VkBool32
+  shaderSignedZeroInfNanPreserveFloat64: VkBool32
+  shaderDenormPreserveFloat16: VkBool32
+  shaderDenormPreserveFloat32: VkBool32
+  shaderDenormPreserveFloat64: VkBool32
+  shaderDenormFlushToZeroFloat16: VkBool32
+  shaderDenormFlushToZeroFloat32: VkBool32
+  shaderDenormFlushToZeroFloat64: VkBool32
+  shaderRoundingModeRTEFloat16: VkBool32
+  shaderRoundingModeRTEFloat32: VkBool32
+  shaderRoundingModeRTEFloat64: VkBool32
+  shaderRoundingModeRTZFloat16: VkBool32
+  shaderRoundingModeRTZFloat32: VkBool32
+  shaderRoundingModeRTZFloat64: VkBool32
+  maxUpdateAfterBindDescriptorsInAllPools: uint32_t
+  shaderUniformBufferArrayNonUniformIndexingNative: VkBool32
+  shaderSampledImageArrayNonUniformIndexingNative: VkBool32
+  shaderStorageBufferArrayNonUniformIndexingNative: VkBool32
+  shaderStorageImageArrayNonUniformIndexingNative: VkBool32
+  shaderInputAttachmentArrayNonUniformIndexingNative: VkBool32
+  robustBufferAccessUpdateAfterBind: VkBool32
+  quadDivergentImplicitLod: VkBool32
+  maxPerStageDescriptorUpdateAfterBindSamplers: uint32_t
+  maxPerStageDescriptorUpdateAfterBindUniformBuffers: uint32_t
+  maxPerStageDescriptorUpdateAfterBindStorageBuffers: uint32_t
+  maxPerStageDescriptorUpdateAfterBindSampledImages: uint32_t
+  maxPerStageDescriptorUpdateAfterBindStorageImages: uint32_t
+  maxPerStageDescriptorUpdateAfterBindInputAttachments: uint32_t
+  maxPerStageUpdateAfterBindResources: uint32_t
+  maxDescriptorSetUpdateAfterBindSamplers: uint32_t
+  maxDescriptorSetUpdateAfterBindUniformBuffers: uint32_t
+  maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: uint32_t
+  maxDescriptorSetUpdateAfterBindStorageBuffers: uint32_t
+  maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: uint32_t
+  maxDescriptorSetUpdateAfterBindSampledImages: uint32_t
+  maxDescriptorSetUpdateAfterBindStorageImages: uint32_t
+  maxDescriptorSetUpdateAfterBindInputAttachments: uint32_t
+  supportedDepthResolveModes: VkResolveModeFlags
+  supportedStencilResolveModes: VkResolveModeFlags
+  independentResolveNone: VkBool32
+  independentResolve: VkBool32
+  filterMinmaxSingleComponentFormats: VkBool32
+  filterMinmaxImageComponentMapping: VkBool32
+  maxTimelineSemaphoreValueDifference: uint64_t
+  framebufferIntegerColorSampleCounts: VkSampleCountFlags
+
+
+@dataclass
+class VkPhysicalDeviceVulkan12Features:
+  samplerMirrorClampToEdge: VkBool32
+  drawIndirectCount: VkBool32
+  storageBuffer8BitAccess: VkBool32
+  uniformAndStorageBuffer8BitAccess: VkBool32
+  storagePushConstant8: VkBool32
+  shaderBufferInt64Atomics: VkBool32
+  shaderSharedInt64Atomics: VkBool32
+  shaderFloat16: VkBool32
+  shaderInt8: VkBool32
+  descriptorIndexing: VkBool32
+  shaderInputAttachmentArrayDynamicIndexing: VkBool32
+  shaderUniformTexelBufferArrayDynamicIndexing: VkBool32
+  shaderStorageTexelBufferArrayDynamicIndexing: VkBool32
+  shaderUniformBufferArrayNonUniformIndexing: VkBool32
+  shaderSampledImageArrayNonUniformIndexing: VkBool32
+  shaderStorageBufferArrayNonUniformIndexing: VkBool32
+  shaderStorageImageArrayNonUniformIndexing: VkBool32
+  shaderInputAttachmentArrayNonUniformIndexing: VkBool32
+  shaderUniformTexelBufferArrayNonUniformIndexing: VkBool32
+  shaderStorageTexelBufferArrayNonUniformIndexing: VkBool32
+  descriptorBindingUniformBufferUpdateAfterBind: VkBool32
+  descriptorBindingSampledImageUpdateAfterBind: VkBool32
+  descriptorBindingStorageImageUpdateAfterBind: VkBool32
+  descriptorBindingStorageBufferUpdateAfterBind: VkBool32
+  descriptorBindingUniformTexelBufferUpdateAfterBind: VkBool32
+  descriptorBindingStorageTexelBufferUpdateAfterBind: VkBool32
+  descriptorBindingUpdateUnusedWhilePending: VkBool32
+  descriptorBindingPartiallyBound: VkBool32
+  descriptorBindingVariableDescriptorCount: VkBool32
+  runtimeDescriptorArray: VkBool32
+  samplerFilterMinmax: VkBool32
+  scalarBlockLayout: VkBool32
+  imagelessFramebuffer: VkBool32
+  uniformBufferStandardLayout: VkBool32
+  shaderSubgroupExtendedTypes: VkBool32
+  separateDepthStencilLayouts: VkBool32
+  hostQueryReset: VkBool32
+  timelineSemaphore: VkBool32
+  bufferDeviceAddress: VkBool32
+  bufferDeviceAddressCaptureReplay: VkBool32
+  bufferDeviceAddressMultiDevice: VkBool32
+  vulkanMemoryModel: VkBool32
+  vulkanMemoryModelDeviceScope: VkBool32
+  vulkanMemoryModelAvailabilityVisibilityChains: VkBool32
+  shaderOutputViewportIndex: VkBool32
+  shaderOutputLayer: VkBool32
+  subgroupBroadcastDynamicId: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVulkan13Properties:
+  minSubgroupSize: uint32_t
+  maxSubgroupSize: uint32_t
+  maxComputeWorkgroupSubgroups: uint32_t
+  requiredSubgroupSizeStages: VkShaderStageFlags
+  maxInlineUniformBlockSize: uint32_t
+  maxPerStageDescriptorInlineUniformBlocks: uint32_t
+  maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks: uint32_t
+  maxDescriptorSetInlineUniformBlocks: uint32_t
+  maxDescriptorSetUpdateAfterBindInlineUniformBlocks: uint32_t
+  maxInlineUniformTotalSize: uint32_t
+  integerDotProduct8BitUnsignedAccelerated: VkBool32
+  integerDotProduct8BitSignedAccelerated: VkBool32
+  integerDotProduct8BitMixedSignednessAccelerated: VkBool32
+  integerDotProduct4x8BitPackedUnsignedAccelerated: VkBool32
+  integerDotProduct4x8BitPackedSignedAccelerated: VkBool32
+  integerDotProduct4x8BitPackedMixedSignednessAccelerated: VkBool32
+  integerDotProduct16BitUnsignedAccelerated: VkBool32
+  integerDotProduct16BitSignedAccelerated: VkBool32
+  integerDotProduct16BitMixedSignednessAccelerated: VkBool32
+  integerDotProduct32BitUnsignedAccelerated: VkBool32
+  integerDotProduct32BitSignedAccelerated: VkBool32
+  integerDotProduct32BitMixedSignednessAccelerated: VkBool32
+  integerDotProduct64BitUnsignedAccelerated: VkBool32
+  integerDotProduct64BitSignedAccelerated: VkBool32
+  integerDotProduct64BitMixedSignednessAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating8BitUnsignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating8BitSignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating16BitUnsignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating16BitSignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating32BitUnsignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating32BitSignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating64BitUnsignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating64BitSignedAccelerated: VkBool32
+  integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated: VkBool32
+  storageTexelBufferOffsetAlignmentBytes: VkDeviceSize
+  storageTexelBufferOffsetSingleTexelAlignment: VkBool32
+  uniformTexelBufferOffsetAlignmentBytes: VkDeviceSize
+  uniformTexelBufferOffsetSingleTexelAlignment: VkBool32
+  maxBufferSize: VkDeviceSize
+
+
+@dataclass
+class VkPhysicalDeviceVulkan13Features:
+  robustImageAccess: VkBool32
+  inlineUniformBlock: VkBool32
+  descriptorBindingInlineUniformBlockUpdateAfterBind: VkBool32
+  pipelineCreationCacheControl: VkBool32
+  privateData: VkBool32
+  shaderDemoteToHelperInvocation: VkBool32
+  shaderTerminateInvocation: VkBool32
+  subgroupSizeControl: VkBool32
+  computeFullSubgroups: VkBool32
+  synchronization2: VkBool32
+  textureCompressionASTC_HDR: VkBool32
+  shaderZeroInitializeWorkgroupMemory: VkBool32
+  dynamicRendering: VkBool32
+  shaderIntegerDotProduct: VkBool32
+  maintenance4: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVulkan14Properties:
+  lineSubPixelPrecisionBits: uint32_t
+  maxVertexAttribDivisor: uint32_t
+  supportsNonZeroFirstInstance: VkBool32
+  maxPushDescriptors: uint32_t
+  dynamicRenderingLocalReadDepthStencilAttachments: VkBool32
+  dynamicRenderingLocalReadMultisampledAttachments: VkBool32
+  earlyFragmentMultisampleCoverageAfterSampleCounting: VkBool32
+  earlyFragmentSampleMaskTestBeforeSampleCounting: VkBool32
+  depthStencilSwizzleOneSupport: VkBool32
+  polygonModePointSize: VkBool32
+  nonStrictSinglePixelWideLinesUseParallelogram: VkBool32
+  nonStrictWideLinesUseParallelogram: VkBool32
+  blockTexelViewCompatibleMultipleLayers: VkBool32
+  maxCombinedImageSamplerDescriptorCount: uint32_t
+  fragmentShadingRateClampCombinerInputs: VkBool32
+  defaultRobustnessStorageBuffers: VkPipelineRobustnessBufferBehavior
+  defaultRobustnessUniformBuffers: VkPipelineRobustnessBufferBehavior
+  defaultRobustnessVertexInputs: VkPipelineRobustnessBufferBehavior
+  defaultRobustnessImages: VkPipelineRobustnessBufferBehavior
+  copySrcLayoutCount: uint32_t
+  pCopySrcLayouts: List[VkImageLayout]
+  copyDstLayoutCount: uint32_t
+  pCopyDstLayouts: List[VkImageLayout]
+  optimalTilingLayoutUUID: uint8_t
+  identicalMemoryTypeRequirements: VkBool32
+
+
+@dataclass
+class VkPhysicalDeviceVulkan14Features:
+  globalPriorityQuery: VkBool32
+  shaderSubgroupRotate: VkBool32
+  shaderSubgroupRotateClustered: VkBool32
+  shaderFloatControls2: VkBool32
+  shaderExpectAssume: VkBool32
+  rectangularLines: VkBool32
+  bresenhamLines: VkBool32
+  smoothLines: VkBool32
+  stippledRectangularLines: VkBool32
+  stippledBresenhamLines: VkBool32
+  stippledSmoothLines: VkBool32
+  vertexAttributeInstanceRateDivisor: VkBool32
+  vertexAttributeInstanceRateZeroDivisor: VkBool32
+  indexTypeUint8: VkBool32
+  dynamicRenderingLocalRead: VkBool32
+  maintenance5: VkBool32
+  maintenance6: VkBool32
+  pipelineProtectedAccess: VkBool32
+  pipelineRobustness: VkBool32
+  hostImageCopy: VkBool32
+  # pushDescriptor: bool
+
+
+@dataclass
+class VkPhysicalDeviceDriverProperties:
+  driverID: VkDriverId
+  driverName: str
+  driverInfo: str
+  conformanceVersion: ConformanceVersion
+
+# Defining alias for structures
+VkPhysicalDeviceLineRasterizationFeaturesEXT = VkPhysicalDeviceLineRasterizationFeatures
+VkPhysicalDeviceLineRasterizationFeaturesKHR = VkPhysicalDeviceLineRasterizationFeatures
+VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR = VkPhysicalDeviceShaderIntegerDotProductFeatures
+VkPhysicalDevice8BitStorageFeaturesKHR = VkPhysicalDevice8BitStorageFeatures
+VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR = VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures
+VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR = VkPhysicalDeviceVertexAttributeDivisorFeatures
+VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT = VkPhysicalDeviceVertexAttributeDivisorFeatures
+VkPhysicalDeviceIndexTypeUint8FeaturesKHR = VkPhysicalDeviceIndexTypeUint8Features
+VkPhysicalDeviceIndexTypeUint8FeaturesEXT = VkPhysicalDeviceIndexTypeUint8Features
+VkPhysicalDeviceVariablePointerFeatures = VkPhysicalDeviceVariablePointersFeatures
+VkPhysicalDeviceVariablePointersFeaturesKHR = VkPhysicalDeviceVariablePointersFeatures
+VkPhysicalDeviceVariablePointerFeaturesKHR = VkPhysicalDeviceVariablePointersFeatures
+VkPhysicalDeviceFloat16Int8FeaturesKHR = VkPhysicalDeviceShaderFloat16Int8Features
+VkPhysicalDeviceShaderFloat16Int8FeaturesKHR = VkPhysicalDeviceShaderFloat16Int8Features
+VkPhysicalDeviceFloatControlsPropertiesKHR = VkPhysicalDeviceFloatControlsProperties
+VkPhysicalDeviceShaderDrawParametersFeatures = VkPhysicalDeviceShaderDrawParameterFeatures
+VkPhysicalDeviceDriverPropertiesKHR = VkPhysicalDeviceDriverProperties
+
+# Defining dependency of structures on extensions
+VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING = {
+  "extensions": {
+    "VK_KHR_variable_pointers": [
+      { "VkPhysicalDeviceVariablePointerFeaturesKHR": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES" },
+      { "VkPhysicalDeviceVariablePointersFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES"},
+    ],
+    "VK_KHR_shader_float16_int8": [
+      { "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES" },
+      {"VkPhysicalDeviceFloat16Int8FeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"},
+    ],
+    "VK_EXT_image_2d_view_of_3d" : [
+      {"VkPhysicalDeviceImage2DViewOf3DFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT"},
+    ],
+    "VK_EXT_custom_border_color" : [
+      {"VkPhysicalDeviceCustomBorderColorFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT"},
+    ],
+    "VK_EXT_primitive_topology_list_restart": [
+      {"VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT"},
+    ],
+    "VK_EXT_provoking_vertex" : [
+      {"VkPhysicalDeviceProvokingVertexFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT"},
+    ],
+    "VK_KHR_index_type_uint8" : [
+      {"VkPhysicalDeviceIndexTypeUint8FeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES"},
+    ],
+    "VK_EXT_index_type_uint8"  : [
+      {"VkPhysicalDeviceIndexTypeUint8FeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES"},
+    ],
+    "VK_KHR_vertex_attribute_divisor" : [
+      {"VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES"},
+    ],
+    "VK_EXT_vertex_attribute_divisor" : [
+      {"VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES"},
+    ],
+    "VK_EXT_transform_feedback" : [
+      {"VkPhysicalDeviceTransformFeedbackFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT"},
+    ],
+    "VK_KHR_shader_subgroup_uniform_control_flow" : [
+      {"VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR"},
+    ],
+    "VK_KHR_shader_subgroup_extended_types" : [
+      {"VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES"},
+    ],
+    "VK_KHR_8bit_storage" : [
+      {"VkPhysicalDevice8BitStorageFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES"},
+    ],
+    "VK_KHR_shader_integer_dot_product" : [
+      {"VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES"},
+    ],
+    "VK_IMG_relaxed_line_rasterization" : [
+      {"VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG"},
+    ],
+    "VK_KHR_line_rasterization" : [
+      {"VkPhysicalDeviceLineRasterizationFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES"},
+    ],
+    "VK_EXT_line_rasterization" : [
+      {"VkPhysicalDeviceLineRasterizationFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES"},
+    ],
+    "VK_EXT_primitives_generated_query" : [
+      {"VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT"},
+    ],
+    "VK_KHR_shader_float_controls" : [
+      {"VkPhysicalDeviceFloatControlsPropertiesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES"},
+    ],
+    "VK_KHR_driver_properties" : [
+      {"VkPhysicalDeviceDriverPropertiesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES"},
+    ]
+  }
+}
+
+# Defining dependency of structures on vulkan cores
+VULKAN_CORES_AND_STRUCTS_MAPPING = {
+  "versions" : {
+    "Core11" : [
+      {"VkPhysicalDeviceVulkan11Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES"},
+      {"VkPhysicalDeviceVulkan11Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES"},
+    ],
+    "Core12" : [
+      {"VkPhysicalDeviceVulkan12Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES"},
+      {"VkPhysicalDeviceVulkan12Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES"},
+    ],
+    "Core13" : [
+      {"VkPhysicalDeviceVulkan13Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES"},
+      {"VkPhysicalDeviceVulkan13Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES"},
+    ],
+    "Core14" : [
+      {"VkPhysicalDeviceVulkan14Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_PROPERTIES"},
+      {"VkPhysicalDeviceVulkan14Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES"},
+    ]
+  }
+}
+
+# Defining map for list type members mapped to its size
+LIST_TYPE_FIELD_AND_SIZE_MAPPING = {
+  "pCopySrcLayouts": "copySrcLayoutCount",
+  "pCopyDstLayouts": "copyDstLayoutCount",
+  "memoryTypes": "memoryTypeCount",
+  "memoryHeaps": "memoryHeapCount",
+}
+
+# Defining dependency of structures on vulkan api version
+VULKAN_VERSIONS_AND_STRUCTS_MAPPING = {
+  "VK_VERSION_1_0" : [
+    {"VkPhysicalDeviceProperties" : "" },
+    {"VkPhysicalDeviceFeatures" : ""},
+    {"VkPhysicalDeviceMemoryProperties" : ""},
+  ],
+  "VK_VERSION_1_1" : [
+    {"VkPhysicalDeviceSubgroupProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES"},
+    {"VkPhysicalDevicePointClippingProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES"},
+    {"VkPhysicalDeviceMultiviewProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES"},
+    {"VkPhysicalDeviceIDProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES"},
+    {"VkPhysicalDeviceMaintenance3Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES"},
+    {"VkPhysicalDeviceMultiviewFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES"},
+    {"VkPhysicalDeviceVariablePointersFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES"},
+    {"VkPhysicalDeviceProtectedMemoryFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES"},
+    {"VkPhysicalDeviceSamplerYcbcrConversionFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES"},
+    {"VkPhysicalDeviceShaderDrawParameterFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES"},
+    {"VkPhysicalDevice16BitStorageFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES"},
+  ]
+}
+
+# List of structures that are not dependent on extensions
+EXTENSION_INDEPENDENT_STRUCTS = [
+  VkPhysicalDeviceProperties,
+  VkPhysicalDeviceFeatures,
+  VkPhysicalDeviceMemoryProperties,
+  VkPhysicalDeviceSubgroupProperties,
+  VkPhysicalDevicePointClippingProperties,
+  VkPhysicalDeviceMultiviewProperties,
+  VkPhysicalDeviceIDProperties,
+  VkPhysicalDeviceMaintenance3Properties,
+  VkPhysicalDevice16BitStorageFeatures,
+  VkPhysicalDeviceMultiviewFeatures,
+  VkPhysicalDeviceVariablePointersFeatures,
+  VkPhysicalDeviceProtectedMemoryFeatures,
+  VkPhysicalDeviceSamplerYcbcrConversionFeatures,
+  VkPhysicalDeviceShaderDrawParameterFeatures,
+]
+
+# List of all the structures for vkjson
+ALL_STRUCTS = [
+  VkPhysicalDeviceFloatControlsPropertiesKHR,
+  VkPhysicalDeviceProperties,
+  VkPhysicalDeviceMemoryProperties,
+  VkPhysicalDeviceSubgroupProperties,
+  VkPhysicalDevicePointClippingProperties,
+  VkPhysicalDeviceMultiviewProperties,
+  VkPhysicalDeviceIDProperties,
+  VkPhysicalDeviceMaintenance3Properties,
+  VkPhysicalDeviceSparseProperties,
+  VkImageFormatProperties,
+  VkQueueFamilyProperties,
+  VkExtensionProperties,
+  VkLayerProperties,
+  VkFormatProperties,
+  VkPhysicalDeviceVariablePointerFeaturesKHR,
+  VkPhysicalDeviceVariablePointersFeaturesKHR,
+  VkPhysicalDeviceShaderFloat16Int8FeaturesKHR,
+  VkPhysicalDeviceFloat16Int8FeaturesKHR,
+  VkPhysicalDeviceImage2DViewOf3DFeaturesEXT,
+  VkPhysicalDeviceCustomBorderColorFeaturesEXT,
+  VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT,
+  VkPhysicalDeviceProvokingVertexFeaturesEXT,
+  VkPhysicalDeviceIndexTypeUint8FeaturesKHR,
+  VkPhysicalDeviceIndexTypeUint8FeaturesEXT,
+  VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR,
+  VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT,
+  VkPhysicalDeviceTransformFeedbackFeaturesEXT,
+  VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR,
+  VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR,
+  VkPhysicalDevice8BitStorageFeaturesKHR,
+  VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR,
+  VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG,
+  VkPhysicalDeviceLineRasterizationFeaturesKHR,
+  VkPhysicalDeviceLineRasterizationFeaturesEXT,
+  VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT,
+  VkPhysicalDevice16BitStorageFeatures,
+  VkPhysicalDeviceMultiviewFeatures,
+  VkPhysicalDeviceProtectedMemoryFeatures,
+  VkPhysicalDeviceSamplerYcbcrConversionFeatures,
+  VkPhysicalDeviceShaderDrawParameterFeatures,
+  VkPhysicalDeviceLimits,
+  VkPhysicalDeviceFeatures,
+  VkPhysicalDeviceVulkan11Properties,
+  VkPhysicalDeviceVulkan11Features,
+  VkPhysicalDeviceVulkan12Properties,
+  VkPhysicalDeviceVulkan12Features,
+  VkPhysicalDeviceVulkan13Properties,
+  VkPhysicalDeviceVulkan13Features,
+  VkPhysicalDeviceVulkan14Properties,
+  VkPhysicalDeviceVulkan14Features,
+  VkPhysicalDeviceDriverProperties,
+]
diff --git a/vulkan/scripts/vkjson_generator.py b/vulkan/scripts/vkjson_generator.py
new file mode 100644
index 0000000..7dc55d9
--- /dev/null
+++ b/vulkan/scripts/vkjson_generator.py
@@ -0,0 +1,1882 @@
+#!/usr/bin/env python3
+#
+# Copyright 2025 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.
+
+"""Generates the vkjson files.
+"""
+import dataclasses
+import os
+import re
+from typing import get_origin
+
+import generator_common as gencom
+import vk as VK
+
+dataclass_field = dataclasses.field
+
+
+COPYRIGHT_WARNINGS = """///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2016 Valve Corporation
+// Copyright (c) 2015-2016 LunarG, Inc.
+// Copyright (c) 2015-2016 Google, Inc.
+//
+// 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.
+///////////////////////////////////////////////////////////////////////////////
+"""
+
+
+def get_copyright_warnings():
+  return COPYRIGHT_WARNINGS
+
+
+def get_vkjson_struct_name(extension_name):
+  """Gets the corresponding structure name from a Vulkan extension name.
+  Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8"
+  """
+  prefix_map = {
+    "VK_KHR": "VkJsonKHR",
+    "VK_EXT": "VkJsonExt",
+    "VK_IMG": "VkJsonIMG"
+  }
+
+  for prefix, replacement in prefix_map.items():
+    if extension_name.startswith(prefix):
+      struct_name = replacement + extension_name[len(prefix):]
+      break
+  else:
+    struct_name = f"VkJsonExt{extension_name}"
+
+  # Convert underscores to camel case
+  # Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8"
+  struct_name = re.sub(r"_(.)", lambda m: m.group(1).upper(), struct_name)
+
+  return struct_name
+
+
+def get_vkjson_struct_variable_name(extension_name):
+  """Gets corresponding instance name from a Vulkan extension name.
+  Example: "VK_KHR_shader_float16_int8" → "khr_shader_float16_int8"
+  """
+  prefix_map = {
+    "VK_KHR_": "khr_",
+    "VK_EXT_": "ext_",
+    "VK_IMG_": "img_"
+  }
+
+  for prefix, replacement in prefix_map.items():
+    if extension_name.startswith(prefix):
+      return replacement + extension_name[len(prefix):]
+
+  return extension_name.lower()  # Default case if no known prefix matches
+
+
+def get_struct_name(struct_name):
+  """Gets corresponding instance name
+  Example: "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR" → "shader_float16_int8_features_khr"
+  """
+  # Remove "VkPhysicalDevice" prefix and any of the known suffixes
+  base_name = struct_name.removeprefix("VkPhysicalDevice").removesuffix("KHR").removesuffix("EXT").removesuffix("IMG")
+
+  # Convert CamelCase to snake_case
+  # Example: "ShaderFloat16Int8Features" → "shader_float16_int8_features"
+  variable_name = re.sub(r"(?<!^)(?=[A-Z])", "_", base_name).lower()
+
+  # Fix special cases
+  variable_name = variable_name.replace("2_d_", "_2d_").replace("3_d_", "_3d_")
+
+  # Add back the correct suffix if it was removed
+  suffix_map = {"KHR": "_khr", "EXT": "_ext", "IMG": "_img"}
+  for suffix, replacement in suffix_map.items():
+    if struct_name.endswith(suffix):
+      variable_name += replacement
+      break
+
+  # Handle specific exceptions
+  special_cases = {
+    "8_bit_storage_features_khr": "bit8_storage_features_khr",
+    "memory_properties": "memory",
+    "16_bit_storage_features": "bit16_storage_features",
+    "i_d_properties": "id_properties"
+  }
+
+  return special_cases.get(variable_name, variable_name)
+
+
+def generate_extension_struct_definition(f):
+  """Generates struct definition code for extension based structs
+  Example:
+  struct VkJsonKHRShaderFloatControls {
+    VkJsonKHRShaderFloatControls() {
+      reported = false;
+      memset(&float_controls_properties_khr, 0,
+            sizeof(VkPhysicalDeviceFloatControlsPropertiesKHR));
+    }
+    bool reported;
+    VkPhysicalDeviceFloatControlsPropertiesKHR float_controls_properties_khr;
+  };
+  """
+  vkJson_entries = []
+
+  for extension_name, struct_list in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
+    vkjson_struct_name = get_vkjson_struct_name(extension_name)
+    vkjson_struct_variable_name = get_vkjson_struct_variable_name(extension_name)
+    vkJson_entries.append(f"{vkjson_struct_name} {vkjson_struct_variable_name}")
+
+    struct_entries = []
+
+    f.write(f"struct {vkjson_struct_name} {{\n")
+    f.write(f"  {vkjson_struct_name}() {{\n")
+    f.write("    reported = false;\n")
+
+    for struct_map in struct_list:
+      for struct_name, _ in struct_map.items():
+        variable_name = get_struct_name(struct_name)
+        f.write(f"    memset(&{variable_name}, 0, sizeof({struct_name}));\n")
+        struct_entries.append(f"{struct_name} {variable_name}")
+
+    f.write("  }\n")  # End of constructor
+    f.write("  bool reported;\n")
+
+    for entry in struct_entries:
+      f.write(f"  {entry};\n")
+
+    f.write("};\n\n")  # End of struct
+
+  return vkJson_entries
+
+
+def generate_vk_core_struct_definition(f):
+  """Generates struct definition code for vulkan cores
+  Example:
+  struct VkJsonCore11 {
+    VkPhysicalDeviceVulkan11Properties properties;
+    VkPhysicalDeviceVulkan11Features features;
+  };
+  """
+  vkJson_core_entries = []
+
+  for version, items in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items():
+    struct_name = f"VkJson{version}"
+    vkJson_core_entries.append(f"{struct_name} {version.lower()}")
+
+    f.write(f"struct {struct_name} {{\n")
+
+    for item in items:
+      for struct_type, _ in item.items():
+        field_name = "properties" if "Properties" in struct_type else "features"
+        f.write(f"  {struct_type} {field_name};\n")
+
+    f.write("};\n\n")
+
+  return vkJson_core_entries
+
+
+def generate_memset_statements(f):
+  """Generates memset statements for all independent Vulkan structs and core Vulkan versions.
+  This initializes struct instances to zero before use.
+
+  Example:
+    memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
+    VkPhysicalDeviceProperties properties;
+  """
+  entries = []
+
+  # Process independent structs
+  for dataclass_type in VK.EXTENSION_INDEPENDENT_STRUCTS:
+    class_name = dataclass_type.__name__
+    variable_name = get_struct_name(class_name)
+    f.write(f"memset(&{variable_name}, 0, sizeof({class_name}));\n")
+    entries.append(f"{class_name} {variable_name}")
+
+  # Process vulkan core structs
+  for version in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"]:
+    struct_name = f"VkJson{version}"
+    f.write(f"memset(&{version.lower()}, 0, sizeof({struct_name}));\n")
+
+  return entries
+
+
+def gen_h():
+  """Generates vkjson.h file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         "..", "vkjson", "vkjson.h")
+
+  with open(genfile, "w") as f:
+    f.write(f'{get_copyright_warnings()}\n')
+
+    f.write("""\
+#ifndef VKJSON_H_
+#define VKJSON_H_
+
+#include <string.h>
+#include <vulkan/vulkan.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#ifdef WIN32
+#undef min
+#undef max
+#endif
+
+/*
+ * This file is autogenerated by vkjson_generator.py. Do not edit directly.
+ */
+struct VkJsonLayer {
+  VkLayerProperties properties;
+  std::vector<VkExtensionProperties> extensions;
+};
+
+\n""")
+
+    vkjson_extension_structs = generate_extension_struct_definition(f)
+    vkjson_core_structs = generate_vk_core_struct_definition(f)
+
+    f.write("""\
+struct VkJsonDevice {
+  VkJsonDevice() {""")
+
+    feature_property_structs = generate_memset_statements(f)
+
+    f.write("""\
+  }\n""")
+    for struct_entries in (vkjson_extension_structs, vkjson_core_structs, feature_property_structs):
+      for entry in struct_entries:
+        f.write(entry + ";\n")
+
+    f.write("""\
+  std::vector<VkQueueFamilyProperties> queues;
+  std::vector<VkExtensionProperties> extensions;
+  std::vector<VkLayerProperties> layers;
+  std::map<VkFormat, VkFormatProperties> formats;
+  std::map<VkExternalFenceHandleTypeFlagBits, VkExternalFenceProperties>
+      external_fence_properties;
+  std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties>
+      external_semaphore_properties;
+};
+
+struct VkJsonDeviceGroup {
+  VkJsonDeviceGroup() {
+    memset(&properties, 0, sizeof(VkPhysicalDeviceGroupProperties));
+  }
+  VkPhysicalDeviceGroupProperties properties;
+  std::vector<uint32_t> device_inds;
+};
+
+struct VkJsonInstance {
+  VkJsonInstance() : api_version(0) {}
+  uint32_t api_version;
+  std::vector<VkJsonLayer> layers;
+  std::vector<VkExtensionProperties> extensions;
+  std::vector<VkJsonDevice> devices;
+  std::vector<VkJsonDeviceGroup> device_groups;
+};
+
+VkJsonInstance VkJsonGetInstance();
+std::string VkJsonInstanceToJson(const VkJsonInstance& instance);
+bool VkJsonInstanceFromJson(const std::string& json,
+                            VkJsonInstance* instance,
+                            std::string* errors);
+
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device);
+std::string VkJsonDeviceToJson(const VkJsonDevice& device);
+bool VkJsonDeviceFromJson(const std::string& json,
+                          VkJsonDevice* device,
+                          std::string* errors);
+
+std::string VkJsonImageFormatPropertiesToJson(
+    const VkImageFormatProperties& properties);
+bool VkJsonImageFormatPropertiesFromJson(const std::string& json,
+                                         VkImageFormatProperties* properties,
+                                         std::string* errors);
+
+// Backward-compatibility aliases
+typedef VkJsonDevice VkJsonAllProperties;
+inline VkJsonAllProperties VkJsonGetAllProperties(
+    VkPhysicalDevice physicalDevice) {
+  return VkJsonGetDevice(physicalDevice);
+}
+inline std::string VkJsonAllPropertiesToJson(
+    const VkJsonAllProperties& properties) {
+  return VkJsonDeviceToJson(properties);
+}
+inline bool VkJsonAllPropertiesFromJson(const std::string& json,
+                                        VkJsonAllProperties* properties,
+                                        std::string* errors) {
+  return VkJsonDeviceFromJson(json, properties, errors);
+}
+
+#endif  // VKJSON_H_""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
+
+
+def generate_extension_struct_template():
+  """Generates templates for extensions
+  Example:
+    template <typename Visitor>
+    inline bool Iterate(Visitor* visitor, VkJsonKHRVariablePointers* structs) {
+      return visitor->Visit("variablePointerFeaturesKHR",
+                            &structs->variable_pointer_features_khr) &&
+            visitor->Visit("variablePointersFeaturesKHR",
+                            &structs->variable_pointers_features_khr);
+    }
+  """
+  template_code = []
+
+  for extension, struct_mappings in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
+    struct_type = get_vkjson_struct_name(extension)
+
+    template_code.append(f"template <typename Visitor>")
+    template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* structs) {{")
+    template_code.append("  return ")
+
+    visitor_calls = []
+    for struct_map in struct_mappings:
+      for struct_name in struct_map:
+        json_field_name = struct_name.replace("VkPhysicalDevice", "")
+        json_field_name = json_field_name[0].lower() + json_field_name[1:]
+
+        # Special case renaming
+        if json_field_name == "8BitStorageFeaturesKHR":
+          json_field_name = "bit8StorageFeaturesKHR"
+
+        visitor_calls.append(
+            f'visitor->Visit("{json_field_name}", &structs->{get_struct_name(struct_name)})'
+        )
+
+    template_code.append(" &&\n         ".join(visitor_calls) + ";")
+    template_code.append("}\n")
+
+  return "\n".join(template_code)
+
+
+def generate_core_template():
+  """Generates templates for vulkan cores.
+  template <typename Visitor>
+  inline bool Iterate(Visitor* visitor, VkJsonCore11* core) {
+    return visitor->Visit("properties", &core->properties) &&
+          visitor->Visit("features", &core->features);
+  }
+  """
+  template_code = []
+
+  for version, struct_list in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items():
+    struct_type = f"VkJson{version}"
+
+    template_code.append(f"template <typename Visitor>")
+    template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* core) {{")
+    template_code.append("  return")
+
+    visitor_calls = []
+    for struct_map in struct_list:
+      for struct_name in struct_map:
+        member_name = "properties" if "Properties" in struct_name else "features"
+        visitor_calls.append(f'visitor->Visit("{member_name}", &core->{member_name})')
+
+    template_code.append(" &&\n         ".join(visitor_calls) + ";")
+    template_code.append("}\n")
+
+  return "\n".join(template_code)
+
+
+def generate_struct_template(data_classes):
+  """Generates templates for all the structs
+  template <typename Visitor>
+  inline bool Iterate(Visitor* visitor,
+                      VkPhysicalDevicePointClippingProperties* properties) {
+    return visitor->Visit("pointClippingBehavior",
+                          &properties->pointClippingBehavior);
+  }
+  """
+  template_code = []
+  processed_classes = set()  # Track processed class names
+
+  for dataclass_type in data_classes:
+    struct_name = dataclass_type.__name__
+
+    if struct_name in processed_classes:
+      continue  # Skip already processed struct
+    processed_classes.add(struct_name)
+
+    struct_fields = dataclasses.fields(dataclass_type)
+    template_code.append("template <typename Visitor>")
+
+    # Determine the correct variable name based on the struct type
+    struct_var = "properties" if "Properties" in struct_name else "features" if "Features" in struct_name else "limits" if "Limits" in struct_name else None
+
+    if not struct_var:
+      continue  # Skip structs that don't match expected patterns
+
+    template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_name}* {struct_var}) {{")
+    template_code.append(f"return\n")
+
+    visitor_calls = []
+    for struct_field in struct_fields:
+      field_name = struct_field.name
+      field_type = struct_field.type
+
+      if get_origin(field_type) is list:
+        # Handle list types (VisitArray)
+        size_field_name = VK.LIST_TYPE_FIELD_AND_SIZE_MAPPING[field_name]
+        visitor_calls.append(f'visitor->VisitArray("{field_name}", {struct_var}->{size_field_name}, &{struct_var}->{field_name})')
+      else:
+        # Handle other types (Visit)
+        visitor_calls.append(f'visitor->Visit("{field_name}", &{struct_var}->{field_name})')
+
+    template_code.append(" &&\n         ".join(visitor_calls) + ";")
+    template_code.append("}\n\n")
+
+  return "\n".join(template_code)
+
+
+def emit_struct_visits_by_vk_version(f, version):
+  """Emits visitor calls for Vulkan version structs
+  """
+  for struct_map in VK.VULKAN_VERSIONS_AND_STRUCTS_MAPPING[version]:
+    for struct_name, _ in struct_map.items():
+      struct_var = get_struct_name(struct_name)
+      # Converts struct_var from snake_case (e.g., point_clipping_properties)
+      # to camelCase (e.g., pointClippingProperties) for struct_display_name.
+      struct_display_name = re.sub(r"_([a-z])", lambda match: match.group(1).upper(), struct_var)
+      f.write(f'visitor->Visit("{struct_display_name}", &device->{struct_var}) &&\n')
+
+
+def gen_cc():
+  """Generates vkjson.cc file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         "..", "vkjson", "vkjson.cc")
+
+  with open(genfile, "w") as f:
+
+    f.write(get_copyright_warnings())
+    f.write("\n")
+
+    f.write("""\
+#include "vkjson.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <json/json.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdio>
+#include <limits>
+#include <memory>
+#include <sstream>
+#include <type_traits>
+#include <utility>
+
+/*
+ * This file is autogenerated by vkjson_generator.py. Do not edit directly.
+ */
+namespace {
+
+/*
+ * Annotation to tell clang that we intend to fall through from one case to
+ * another in a switch. Sourced from android-base/macros.h.
+ */
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+
+inline bool IsIntegral(double value) {
+#if defined(ANDROID)
+  // Android NDK doesn't provide std::trunc yet
+  return trunc(value) == value;
+#else
+  return std::trunc(value) == value;
+#endif
+}
+
+// Floating point fields of Vulkan structure use single precision. The string
+// output of max double value in c++ will be larger than Java double's infinity
+// value. Below fake double max/min values are only to serve the safe json text
+// parsing in between C++ and Java, because Java json library simply cannot
+// handle infinity.
+static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max();
+static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX;
+
+template <typename T> struct EnumTraits;
+template <> struct EnumTraits<VkPhysicalDeviceType> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+      case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_CPU:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <> struct EnumTraits<VkFormat> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_FORMAT_UNDEFINED:
+      case VK_FORMAT_R4G4_UNORM_PACK8:
+      case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
+      case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
+      case VK_FORMAT_R5G6B5_UNORM_PACK16:
+      case VK_FORMAT_B5G6R5_UNORM_PACK16:
+      case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
+      case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
+      case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
+      case VK_FORMAT_R8_UNORM:
+      case VK_FORMAT_R8_SNORM:
+      case VK_FORMAT_R8_USCALED:
+      case VK_FORMAT_R8_SSCALED:
+      case VK_FORMAT_R8_UINT:
+      case VK_FORMAT_R8_SINT:
+      case VK_FORMAT_R8_SRGB:
+      case VK_FORMAT_R8G8_UNORM:
+      case VK_FORMAT_R8G8_SNORM:
+      case VK_FORMAT_R8G8_USCALED:
+      case VK_FORMAT_R8G8_SSCALED:
+      case VK_FORMAT_R8G8_UINT:
+      case VK_FORMAT_R8G8_SINT:
+      case VK_FORMAT_R8G8_SRGB:
+      case VK_FORMAT_R8G8B8_UNORM:
+      case VK_FORMAT_R8G8B8_SNORM:
+      case VK_FORMAT_R8G8B8_USCALED:
+      case VK_FORMAT_R8G8B8_SSCALED:
+      case VK_FORMAT_R8G8B8_UINT:
+      case VK_FORMAT_R8G8B8_SINT:
+      case VK_FORMAT_R8G8B8_SRGB:
+      case VK_FORMAT_B8G8R8_UNORM:
+      case VK_FORMAT_B8G8R8_SNORM:
+      case VK_FORMAT_B8G8R8_USCALED:
+      case VK_FORMAT_B8G8R8_SSCALED:
+      case VK_FORMAT_B8G8R8_UINT:
+      case VK_FORMAT_B8G8R8_SINT:
+      case VK_FORMAT_B8G8R8_SRGB:
+      case VK_FORMAT_R8G8B8A8_UNORM:
+      case VK_FORMAT_R8G8B8A8_SNORM:
+      case VK_FORMAT_R8G8B8A8_USCALED:
+      case VK_FORMAT_R8G8B8A8_SSCALED:
+      case VK_FORMAT_R8G8B8A8_UINT:
+      case VK_FORMAT_R8G8B8A8_SINT:
+      case VK_FORMAT_R8G8B8A8_SRGB:
+      case VK_FORMAT_B8G8R8A8_UNORM:
+      case VK_FORMAT_B8G8R8A8_SNORM:
+      case VK_FORMAT_B8G8R8A8_USCALED:
+      case VK_FORMAT_B8G8R8A8_SSCALED:
+      case VK_FORMAT_B8G8R8A8_UINT:
+      case VK_FORMAT_B8G8R8A8_SINT:
+      case VK_FORMAT_B8G8R8A8_SRGB:
+      case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
+      case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
+      case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
+      case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
+      case VK_FORMAT_A8B8G8R8_UINT_PACK32:
+      case VK_FORMAT_A8B8G8R8_SINT_PACK32:
+      case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+      case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+      case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
+      case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
+      case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
+      case VK_FORMAT_A2R10G10B10_UINT_PACK32:
+      case VK_FORMAT_A2R10G10B10_SINT_PACK32:
+      case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+      case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
+      case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
+      case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
+      case VK_FORMAT_A2B10G10R10_UINT_PACK32:
+      case VK_FORMAT_A2B10G10R10_SINT_PACK32:
+      case VK_FORMAT_R16_UNORM:
+      case VK_FORMAT_R16_SNORM:
+      case VK_FORMAT_R16_USCALED:
+      case VK_FORMAT_R16_SSCALED:
+      case VK_FORMAT_R16_UINT:
+      case VK_FORMAT_R16_SINT:
+      case VK_FORMAT_R16_SFLOAT:
+      case VK_FORMAT_R16G16_UNORM:
+      case VK_FORMAT_R16G16_SNORM:
+      case VK_FORMAT_R16G16_USCALED:
+      case VK_FORMAT_R16G16_SSCALED:
+      case VK_FORMAT_R16G16_UINT:
+      case VK_FORMAT_R16G16_SINT:
+      case VK_FORMAT_R16G16_SFLOAT:
+      case VK_FORMAT_R16G16B16_UNORM:
+      case VK_FORMAT_R16G16B16_SNORM:
+      case VK_FORMAT_R16G16B16_USCALED:
+      case VK_FORMAT_R16G16B16_SSCALED:
+      case VK_FORMAT_R16G16B16_UINT:
+      case VK_FORMAT_R16G16B16_SINT:
+      case VK_FORMAT_R16G16B16_SFLOAT:
+      case VK_FORMAT_R16G16B16A16_UNORM:
+      case VK_FORMAT_R16G16B16A16_SNORM:
+      case VK_FORMAT_R16G16B16A16_USCALED:
+      case VK_FORMAT_R16G16B16A16_SSCALED:
+      case VK_FORMAT_R16G16B16A16_UINT:
+      case VK_FORMAT_R16G16B16A16_SINT:
+      case VK_FORMAT_R16G16B16A16_SFLOAT:
+      case VK_FORMAT_R32_UINT:
+      case VK_FORMAT_R32_SINT:
+      case VK_FORMAT_R32_SFLOAT:
+      case VK_FORMAT_R32G32_UINT:
+      case VK_FORMAT_R32G32_SINT:
+      case VK_FORMAT_R32G32_SFLOAT:
+      case VK_FORMAT_R32G32B32_UINT:
+      case VK_FORMAT_R32G32B32_SINT:
+      case VK_FORMAT_R32G32B32_SFLOAT:
+      case VK_FORMAT_R32G32B32A32_UINT:
+      case VK_FORMAT_R32G32B32A32_SINT:
+      case VK_FORMAT_R32G32B32A32_SFLOAT:
+      case VK_FORMAT_R64_UINT:
+      case VK_FORMAT_R64_SINT:
+      case VK_FORMAT_R64_SFLOAT:
+      case VK_FORMAT_R64G64_UINT:
+      case VK_FORMAT_R64G64_SINT:
+      case VK_FORMAT_R64G64_SFLOAT:
+      case VK_FORMAT_R64G64B64_UINT:
+      case VK_FORMAT_R64G64B64_SINT:
+      case VK_FORMAT_R64G64B64_SFLOAT:
+      case VK_FORMAT_R64G64B64A64_UINT:
+      case VK_FORMAT_R64G64B64A64_SINT:
+      case VK_FORMAT_R64G64B64A64_SFLOAT:
+      case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
+      case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
+      case VK_FORMAT_D16_UNORM:
+      case VK_FORMAT_X8_D24_UNORM_PACK32:
+      case VK_FORMAT_D32_SFLOAT:
+      case VK_FORMAT_S8_UINT:
+      case VK_FORMAT_D16_UNORM_S8_UINT:
+      case VK_FORMAT_D24_UNORM_S8_UINT:
+      case VK_FORMAT_D32_SFLOAT_S8_UINT:
+      case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+      case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+      case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+      case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+      case VK_FORMAT_BC2_UNORM_BLOCK:
+      case VK_FORMAT_BC2_SRGB_BLOCK:
+      case VK_FORMAT_BC3_UNORM_BLOCK:
+      case VK_FORMAT_BC3_SRGB_BLOCK:
+      case VK_FORMAT_BC4_UNORM_BLOCK:
+      case VK_FORMAT_BC4_SNORM_BLOCK:
+      case VK_FORMAT_BC5_UNORM_BLOCK:
+      case VK_FORMAT_BC5_SNORM_BLOCK:
+      case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+      case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+      case VK_FORMAT_BC7_UNORM_BLOCK:
+      case VK_FORMAT_BC7_SRGB_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+      case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+      case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+      case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+      case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+      case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+      case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+      case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+      case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+      case VK_FORMAT_G8B8G8R8_422_UNORM:
+      case VK_FORMAT_B8G8R8G8_422_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+      case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
+      case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+      case VK_FORMAT_R10X6_UNORM_PACK16:
+      case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
+      case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+      case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
+      case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
+      case VK_FORMAT_R12X4_UNORM_PACK16:
+      case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
+      case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
+      case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
+      case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
+      case VK_FORMAT_G16B16G16R16_422_UNORM:
+      case VK_FORMAT_B16G16R16G16_422_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
+      case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
+      case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
+      case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
+      case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
+      case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
+      case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
+      case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
+      case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
+      case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
+      case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
+      case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
+      case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkPointClippingBehavior> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
+      case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkExternalFenceHandleTypeFlagBits> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT:
+      case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT:
+      case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT:
+      case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkExternalSemaphoreHandleTypeFlagBits> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
+      case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT:
+      case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT:
+      case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT:
+      case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkDriverIdKHR> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_DRIVER_ID_AMD_PROPRIETARY:
+      case VK_DRIVER_ID_AMD_OPEN_SOURCE:
+      case VK_DRIVER_ID_MESA_RADV:
+      case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+      case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+      case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
+      case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
+      case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
+      case VK_DRIVER_ID_ARM_PROPRIETARY:
+      case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
+      case VK_DRIVER_ID_GGP_PROPRIETARY:
+      case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
+      case VK_DRIVER_ID_MESA_LLVMPIPE:
+      case VK_DRIVER_ID_MOLTENVK:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkShaderFloatControlsIndependence> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkPipelineRobustnessBufferBehavior> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkPipelineRobustnessImageBehavior> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DISABLED:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkImageLayout> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_IMAGE_LAYOUT_UNDEFINED:
+      case VK_IMAGE_LAYOUT_GENERAL:
+      case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+      case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+      case VK_IMAGE_LAYOUT_PREINITIALIZED:
+      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_SRC_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR:
+      case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR:
+      case VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT:
+      case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DST_KHR:
+#endif
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR:
+#endif
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR:
+#endif
+      case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:
+        return true;
+    }
+    return false;
+  }
+};
+
+// VkSparseImageFormatProperties
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkExtent3D* extents) {
+  return
+    visitor->Visit("width", &extents->width) &&
+    visitor->Visit("height", &extents->height) &&
+    visitor->Visit("depth", &extents->depth);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkConformanceVersionKHR* version) {
+  return visitor->Visit("major", &version->major) &&
+         visitor->Visit("minor", &version->minor) &&
+         visitor->Visit("subminor", &version->subminor) &&
+         visitor->Visit("patch", &version->patch);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkMemoryType* type) {
+  return
+    visitor->Visit("propertyFlags", &type->propertyFlags) &&
+    visitor->Visit("heapIndex", &type->heapIndex);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkMemoryHeap* heap) {
+  return
+    visitor->Visit("size", &heap->size) &&
+    visitor->Visit("flags", &heap->flags);
+}\n\n""")
+
+    f.write(f"{generate_core_template()}\n\n{generate_extension_struct_template()}\n\n")
+    f.write(generate_struct_template(VK.ALL_STRUCTS))
+
+    f.write("""\
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkExternalFenceProperties* properties) {
+  return visitor->Visit("exportFromImportedHandleTypes",
+                        &properties->exportFromImportedHandleTypes) &&
+         visitor->Visit("compatibleHandleTypes",
+                        &properties->compatibleHandleTypes) &&
+         visitor->Visit("externalFenceFeatures",
+                        &properties->externalFenceFeatures);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkExternalSemaphoreProperties* properties) {
+  return visitor->Visit("exportFromImportedHandleTypes",
+                        &properties->exportFromImportedHandleTypes) &&
+         visitor->Visit("compatibleHandleTypes",
+                        &properties->compatibleHandleTypes) &&
+         visitor->Visit("externalSemaphoreFeatures",
+                        &properties->externalSemaphoreFeatures);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonLayer* layer) {
+  return visitor->Visit("properties", &layer->properties) &&
+         visitor->Visit("extensions", &layer->extensions);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonDeviceGroup* device_group) {
+  return visitor->Visit("devices", &device_group->device_inds) &&
+         visitor->Visit("subsetAllocation",
+                        &device_group->properties.subsetAllocation);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonDevice* device) {
+  bool ret = true;
+  switch (device->properties.apiVersion ^
+          VK_API_VERSION_PATCH(device->properties.apiVersion)) {
+    case VK_API_VERSION_1_4:
+      ret &= visitor->Visit("core14", &device->core14);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_3:
+      ret &= visitor->Visit("core13", &device->core13);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_2:
+      ret &= visitor->Visit("core11", &device->core11);
+      ret &= visitor->Visit("core12", &device->core12);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_1:
+      ret &=\n""")
+
+    emit_struct_visits_by_vk_version(f, "VK_VERSION_1_1")
+
+    f.write("""\
+          visitor->Visit("externalFenceProperties",
+                         &device->external_fence_properties) &&
+          visitor->Visit("externalSemaphoreProperties",
+                         &device->external_semaphore_properties);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_0:
+      ret &=\n""")
+
+    emit_struct_visits_by_vk_version(f, "VK_VERSION_1_0")
+
+    f.write("""\
+             visitor->Visit("queues", &device->queues) &&
+             visitor->Visit("extensions", &device->extensions) &&
+             visitor->Visit("layers", &device->layers) &&
+             visitor->Visit("formats", &device->formats);\n\n""")
+
+    for extension_name, _ in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
+      struct_var = get_vkjson_struct_variable_name(extension_name)
+      f.write(f"  if (device->{struct_var}.reported) {{\n")
+      f.write(f"    ret &= visitor->Visit(\"{extension_name}\", &device->{struct_var});\n")
+      f.write("  }\n")
+
+    f.write("""\
+    } return ret; }
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
+  bool ret = true;
+  switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) {
+    case VK_API_VERSION_1_4:
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_3:
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_2:
+      ret &= visitor->Visit("apiVersion", &instance->api_version);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_1:
+      ret &= visitor->Visit("deviceGroups", &instance->device_groups);
+      FALLTHROUGH_INTENDED;
+    case VK_API_VERSION_1_0:
+      char depString[] =
+          "vkjson is deprecated, and will be replaced in a future release";
+      ret &= visitor->Visit("layers", &instance->layers) &&
+             visitor->Visit("extensions", &instance->extensions) &&
+             visitor->Visit("devices", &instance->devices) &&
+             visitor->Visit("_comment", &depString);
+  }
+  return ret;
+}
+
+template <typename T>
+using EnableForArithmetic =
+    typename std::enable_if<std::is_arithmetic<T>::value, void>::type;
+
+template <typename T>
+using EnableForStruct =
+    typename std::enable_if<std::is_class<T>::value, void>::type;
+
+template <typename T>
+using EnableForEnum =
+    typename std::enable_if<std::is_enum<T>::value, void>::type;
+
+template <typename T, typename = EnableForStruct<T>, typename = void>
+Json::Value ToJsonValue(const T& value);
+
+template <typename T, typename = EnableForArithmetic<T>>
+inline Json::Value ToJsonValue(const T& value) {
+  return Json::Value(
+      std::clamp(static_cast<double>(value), SAFE_DOUBLE_MIN, SAFE_DOUBLE_MAX));
+}
+
+inline Json::Value ToJsonValue(const uint64_t& value) {
+  char string[19] = {0};  // "0x" + 16 digits + terminal \\0
+  snprintf(string, sizeof(string), "0x%016" PRIx64, value);
+  return Json::Value(string);
+}
+
+template <typename T, typename = EnableForEnum<T>, typename = void,
+          typename = void>
+inline Json::Value ToJsonValue(const T& value) {
+  return Json::Value(static_cast<double>(value));
+}
+
+template <typename T>
+inline Json::Value ArrayToJsonValue(uint32_t count, const T* values) {
+  Json::Value array(Json::arrayValue);
+  for (unsigned int i = 0; i < count; ++i) array.append(ToJsonValue(values[i]));
+  return array;
+}
+
+template <typename T, size_t N>
+inline Json::Value ToJsonValue(const T (&value)[N]) {
+  return ArrayToJsonValue(N, value);
+}
+
+template <size_t N>
+inline Json::Value ToJsonValue(const char (&value)[N]) {
+  assert(strlen(value) < N);
+  return Json::Value(value);
+}
+
+template <typename T>
+inline Json::Value ToJsonValue(const std::vector<T>& value) {
+  assert(value.size() <= std::numeric_limits<uint32_t>::max());
+  return ArrayToJsonValue(static_cast<uint32_t>(value.size()), value.data());
+}
+
+template <typename F, typename S>
+inline Json::Value ToJsonValue(const std::pair<F, S>& value) {
+  Json::Value array(Json::arrayValue);
+  array.append(ToJsonValue(value.first));
+  array.append(ToJsonValue(value.second));
+  return array;
+}
+
+template <typename F, typename S>
+inline Json::Value ToJsonValue(const std::map<F, S>& value) {
+  Json::Value array(Json::arrayValue);
+  for (auto& kv : value) array.append(ToJsonValue(kv));
+  return array;
+}
+
+class JsonWriterVisitor {
+ public:
+  JsonWriterVisitor() : object_(Json::objectValue) {}
+
+  ~JsonWriterVisitor() {}
+
+  template <typename T> bool Visit(const char* key, const T* value) {
+    object_[key] = ToJsonValue(*value);
+    return true;
+  }
+
+  template <typename T, uint32_t N>
+  bool VisitArray(const char* key, uint32_t count, const T (*value)[N]) {
+    assert(count <= N);
+    object_[key] = ArrayToJsonValue(count, *value);
+    return true;
+  }
+
+  template <typename T>
+  bool VisitArray(const char* key, uint32_t count, const T *value) {
+    object_[key] = ArrayToJsonValue(count, *value);
+    return true;
+  }
+
+  Json::Value get_object() const { return object_; }
+
+ private:
+  Json::Value object_;
+};
+
+template <typename Visitor, typename T>
+inline void VisitForWrite(Visitor* visitor, const T& t) {
+  Iterate(visitor, const_cast<T*>(&t));
+}
+
+template <typename T, typename /*= EnableForStruct<T>*/, typename /*= void*/>
+Json::Value ToJsonValue(const T& value) {
+  JsonWriterVisitor visitor;
+  VisitForWrite(&visitor, value);
+  return visitor.get_object();
+}
+
+template <typename T, typename = EnableForStruct<T>>
+bool AsValue(Json::Value* json_value, T* t);
+
+inline bool AsValue(Json::Value* json_value, int32_t* value) {
+  if (json_value->type() != Json::realValue) return false;
+  double d = json_value->asDouble();
+  if (!IsIntegral(d) ||
+      d < static_cast<double>(std::numeric_limits<int32_t>::min()) ||
+      d > static_cast<double>(std::numeric_limits<int32_t>::max()))
+    return false;
+  *value = static_cast<int32_t>(d);
+  return true;
+}
+
+inline bool AsValue(Json::Value* json_value, uint64_t* value) {
+  if (json_value->type() != Json::stringValue) return false;
+  int result =
+      std::sscanf(json_value->asString().c_str(), "0x%016" PRIx64, value);
+  return result == 1;
+}
+
+inline bool AsValue(Json::Value* json_value, uint32_t* value) {
+  if (json_value->type() != Json::realValue) return false;
+  double d = json_value->asDouble();
+  if (!IsIntegral(d) || d < 0.0 ||
+      d > static_cast<double>(std::numeric_limits<uint32_t>::max()))
+    return false;
+  *value = static_cast<uint32_t>(d);
+  return true;
+}
+
+inline bool AsValue(Json::Value* json_value, uint8_t* value) {
+  uint32_t value32 = 0;
+  AsValue(json_value, &value32);
+  if (value32 > std::numeric_limits<uint8_t>::max())
+    return false;
+  *value = static_cast<uint8_t>(value32);
+  return true;
+}
+
+inline bool AsValue(Json::Value* json_value, float* value) {
+  if (json_value->type() != Json::realValue) return false;
+  *value = static_cast<float>(json_value->asDouble());
+  return true;
+}
+
+inline bool AsValue(Json::Value* json_value, VkImageLayout* t) {
+  uint32_t value = 0;
+  if (!AsValue(json_value, &value))
+    return false;
+  if (!EnumTraits<VkImageLayout>::exist(value)) return false;
+  *t = static_cast<VkImageLayout>(value);
+  return true;
+}
+
+template <typename T>
+inline bool AsArray(Json::Value* json_value, uint32_t count, T* values) {
+  if (json_value->type() != Json::arrayValue || json_value->size() != count)
+    return false;
+  for (uint32_t i = 0; i < count; ++i) {
+    if (!AsValue(&(*json_value)[i], values + i)) return false;
+  }
+  return true;
+}
+
+template <typename T, size_t N>
+inline bool AsValue(Json::Value* json_value, T (*value)[N]) {
+  return AsArray(json_value, N, *value);
+}
+
+template <size_t N>
+inline bool AsValue(Json::Value* json_value, char (*value)[N]) {
+  if (json_value->type() != Json::stringValue) return false;
+  size_t len = json_value->asString().length();
+  if (len >= N)
+    return false;
+  memcpy(*value, json_value->asString().c_str(), len);
+  memset(*value + len, 0, N-len);
+  return true;
+}
+
+template <typename T, typename = EnableForEnum<T>, typename = void>
+inline bool AsValue(Json::Value* json_value, T* t) {
+  uint32_t value = 0;
+  if (!AsValue(json_value, &value))
+      return false;
+  if (!EnumTraits<T>::exist(value)) return false;
+  *t = static_cast<T>(value);
+  return true;
+}
+
+template <typename T>
+inline bool AsValue(Json::Value* json_value, std::vector<T>* value) {
+  if (json_value->type() != Json::arrayValue) return false;
+  int size = json_value->size();
+  value->resize(size);
+  return AsArray(json_value, size, value->data());
+}
+
+template <typename F, typename S>
+inline bool AsValue(Json::Value* json_value, std::pair<F, S>* value) {
+  if (json_value->type() != Json::arrayValue || json_value->size() != 2)
+    return false;
+  return AsValue(&(*json_value)[0], &value->first) &&
+         AsValue(&(*json_value)[1], &value->second);
+}
+
+template <typename F, typename S>
+inline bool AsValue(Json::Value* json_value, std::map<F, S>* value) {
+  if (json_value->type() != Json::arrayValue) return false;
+  int size = json_value->size();
+  for (int i = 0; i < size; ++i) {
+    std::pair<F, S> elem;
+    if (!AsValue(&(*json_value)[i], &elem)) return false;
+    if (!value->insert(elem).second)
+      return false;
+  }
+  return true;
+}
+
+template <typename T>
+bool ReadValue(Json::Value* object, const char* key, T* value,
+               std::string* errors) {
+  Json::Value json_value = (*object)[key];
+  if (!json_value) {
+    if (errors)
+      *errors = std::string(key) + " missing.";
+    return false;
+  }
+  if (AsValue(&json_value, value)) return true;
+  if (errors)
+    *errors = std::string("Wrong type for ") + std::string(key) + ".";
+  return false;
+}
+
+template <typename Visitor, typename T>
+inline bool VisitForRead(Visitor* visitor, T* t) {
+  return Iterate(visitor, t);
+}
+
+class JsonReaderVisitor {
+ public:
+  JsonReaderVisitor(Json::Value* object, std::string* errors)
+      : object_(object), errors_(errors) {}
+
+  template <typename T> bool Visit(const char* key, T* value) const {
+    return ReadValue(object_, key, value, errors_);
+  }
+
+  template <typename T, uint32_t N>
+  bool VisitArray(const char* key, uint32_t count, T (*value)[N]) {
+    if (count > N)
+      return false;
+    Json::Value json_value = (*object_)[key];
+    if (!json_value) {
+      if (errors_)
+        *errors_ = std::string(key) + " missing.";
+      return false;
+    }
+    if (AsArray(&json_value, count, *value)) return true;
+    if (errors_)
+      *errors_ = std::string("Wrong type for ") + std::string(key) + ".";
+    return false;
+  }
+
+  template <typename T>
+  bool VisitArray(const char* key, uint32_t count, T *value) {
+    Json::Value json_value = (*object_)[key];
+    if (!json_value) {
+      if (errors_)
+        *errors_ = std::string(key) + " missing.";
+      return false;
+    }
+    if (AsArray(&json_value, count, *value)) return true;
+    if (errors_)
+      *errors_ = std::string("Wrong type for ") + std::string(key) + ".";
+    return false;
+  }
+
+
+ private:
+  Json::Value* object_;
+  std::string* errors_;
+};
+
+template <typename T, typename /*= EnableForStruct<T>*/>
+bool AsValue(Json::Value* json_value, T* t) {
+  if (json_value->type() != Json::objectValue) return false;
+  JsonReaderVisitor visitor(json_value, nullptr);
+  return VisitForRead(&visitor, t);
+}
+
+
+template <typename T> std::string VkTypeToJson(const T& t) {
+  JsonWriterVisitor visitor;
+  VisitForWrite(&visitor, t);
+  return visitor.get_object().toStyledString();
+}
+
+template <typename T> bool VkTypeFromJson(const std::string& json,
+                                          T* t,
+                                          std::string* errors) {
+  *t = T();
+  Json::Value object(Json::objectValue);
+  Json::CharReaderBuilder builder;
+  builder["collectComments"] = false;
+  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+  if (!reader->parse(json.data(), json.data() + json.size(), &object, errors)) {
+    return false;
+  }
+  return AsValue(&object, t);
+}
+
+}  // anonymous namespace
+
+std::string VkJsonInstanceToJson(const VkJsonInstance& instance) {
+  return VkTypeToJson(instance);
+}
+
+bool VkJsonInstanceFromJson(const std::string& json,
+                            VkJsonInstance* instance,
+                            std::string* errors) {
+  return VkTypeFromJson(json, instance, errors);
+}
+
+std::string VkJsonDeviceToJson(const VkJsonDevice& device) {
+  return VkTypeToJson(device);
+}
+
+bool VkJsonDeviceFromJson(const std::string& json,
+                          VkJsonDevice* device,
+                          std::string* errors) {
+  return VkTypeFromJson(json, device, errors);
+};
+
+std::string VkJsonImageFormatPropertiesToJson(
+    const VkImageFormatProperties& properties) {
+  return VkTypeToJson(properties);
+}
+
+bool VkJsonImageFormatPropertiesFromJson(const std::string& json,
+                                         VkImageFormatProperties* properties,
+                                         std::string* errors) {
+  return VkTypeFromJson(json, properties, errors);
+};
+""")
+    f.close()
+  gencom.run_clang_format(genfile)
+
+
+def generate_vk_core_structs_init_code(version):
+  """Generates code to initialize properties and features
+  for structs based on its vulkan API version dependency.
+  """
+  properties_code, features_code = [], []
+
+  for item in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].get(version, []):
+    for struct_name, struct_type in item.items():
+      version_lower = version.lower()
+
+      if "Properties" in struct_name:
+        properties_code.extend([
+            f"device.{version_lower}.properties.sType = {struct_type};",
+            f"device.{version_lower}.properties.pNext = properties.pNext;",
+            f"properties.pNext = &device.{version_lower}.properties;\n\n"
+        ])
+
+      elif "Features" in struct_name:
+        features_code.extend([
+            f"device.{version_lower}.features.sType = {struct_type};",
+            f"device.{version_lower}.features.pNext = features.pNext;",
+            f"features.pNext = &device.{version_lower}.features;\n\n"
+        ])
+
+  return "\n".join(properties_code), "\n".join(features_code)
+
+
+def generate_vk_extension_structs_init_code(mapping, struct_category, next_pointer):
+  """Generates Vulkan struct initialization code for given struct category (Properties/Features)
+  based on its extension dependency.
+  """
+  generated_code = []
+
+  for extension, struct_mappings in mapping.items():
+    struct_var_name = get_vkjson_struct_variable_name(extension)
+    extension_code = [
+        f"  if (HasExtension(\"{extension}\", device.extensions)) {{",
+        f"    device.{struct_var_name}.reported = true;"
+    ]
+
+    struct_count = 0
+    for struct_mapping in struct_mappings:
+      for struct_name, struct_type in struct_mapping.items():
+        if struct_category in struct_name:
+          struct_count += 1
+          struct_instance = get_struct_name(struct_name)
+          extension_code.extend([
+              f"    device.{struct_var_name}.{struct_instance}.sType = {struct_type};",
+              f"    device.{struct_var_name}.{struct_instance}.pNext = {next_pointer}.pNext;",
+              f"    {next_pointer}.pNext = &device.{struct_var_name}.{struct_instance};"
+          ])
+
+    extension_code.append("  }\n")
+
+    if struct_count > 0:
+      generated_code.extend(extension_code)
+
+  return "\n".join(generated_code)
+
+
+def generate_vk_version_structs_initialization(version_data, struct_type_keyword, next_pointer):
+  """Generates Vulkan struct initialization code for given struct category (Properties/Features)
+  of vulkan api version s.
+  """
+  struct_initialization_code = []
+
+  for struct_mapping in version_data:
+    for struct_name, struct_type in struct_mapping.items():
+      if struct_type_keyword in struct_name:
+        struct_variable = get_struct_name(struct_name)
+        struct_initialization_code.extend([
+            f"device.{struct_variable}.sType = {struct_type};",
+            f"device.{struct_variable}.pNext = {next_pointer}.pNext;",
+            f"{next_pointer}.pNext = &device.{struct_variable};\n"
+        ])
+
+  return "\n".join(struct_initialization_code)
+
+
+def gen_instance_cc():
+  """Generates vkjson_instance.cc file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         "..", "vkjson", "vkjson_instance.cc")
+
+  with open(genfile, "w") as f:
+    f.write(get_copyright_warnings())
+    f.write("\n")
+
+    f.write("""\
+#ifndef VK_PROTOTYPES
+#define VK_PROTOTYPES
+#endif
+
+#include "vkjson.h"
+
+#include <algorithm>
+#include <utility>
+
+/*
+ * This file is autogenerated by vkjson_generator.py. Do not edit directly.
+ */
+namespace {
+
+bool EnumerateExtensions(const char* layer_name,
+                         std::vector<VkExtensionProperties>* extensions) {
+  VkResult result;
+  uint32_t count = 0;
+  result = vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr);
+  if (result != VK_SUCCESS)
+    return false;
+  extensions->resize(count);
+  result = vkEnumerateInstanceExtensionProperties(layer_name, &count,
+                                                  extensions->data());
+  if (result != VK_SUCCESS)
+    return false;
+  return true;
+}
+
+bool HasExtension(const char* extension_name,
+                  const std::vector<VkExtensionProperties>& extensions) {
+  return std::find_if(extensions.cbegin(), extensions.cend(),
+                      [extension_name](const VkExtensionProperties& extension) {
+                        return strcmp(extension.extensionName,
+                                      extension_name) == 0;
+                      }) != extensions.cend();
+}
+}  // anonymous namespace
+
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) {
+  VkJsonDevice device;
+
+  uint32_t extension_count = 0;
+  vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
+                                       &extension_count, nullptr);
+  if (extension_count > 0) {
+    device.extensions.resize(extension_count);
+    vkEnumerateDeviceExtensionProperties(
+        physical_device, nullptr, &extension_count, device.extensions.data());
+  }
+
+  uint32_t layer_count = 0;
+  vkEnumerateDeviceLayerProperties(physical_device, &layer_count, nullptr);
+  if (layer_count > 0) {
+    device.layers.resize(layer_count);
+    vkEnumerateDeviceLayerProperties(physical_device, &layer_count,
+                                     device.layers.data());
+  }
+
+  VkPhysicalDeviceProperties2 properties = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+      nullptr,
+      {},
+  };\n\n""")
+
+    cc_code_properties = generate_vk_extension_structs_init_code(
+    VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"], "Properties", "properties"
+    )
+    f.write(f'{cc_code_properties}\n')
+
+    f.write("""\
+
+  vkGetPhysicalDeviceProperties2(physical_device, &properties);
+  device.properties = properties.properties;
+
+  VkPhysicalDeviceFeatures2 features = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+      nullptr,
+      {},
+  };\n\n""")
+
+    cc_code_features = generate_vk_extension_structs_init_code(
+      VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"], "Features", "features"
+    )
+    f.write(f'{cc_code_features}\n')
+
+    f.write("""\
+
+  vkGetPhysicalDeviceFeatures2(physical_device, &features);
+  device.features = features.features;
+
+  vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory);
+
+  uint32_t queue_family_count = 0;
+  vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count,
+                                           nullptr);
+  if (queue_family_count > 0) {
+    device.queues.resize(queue_family_count);
+    vkGetPhysicalDeviceQueueFamilyProperties(
+        physical_device, &queue_family_count, device.queues.data());
+  }
+
+  VkFormatProperties format_properties = {};
+  for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8;
+       // TODO(http://b/171403054): avoid hard-coding last value in the
+       // contiguous range
+       format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
+       format = static_cast<VkFormat>(format + 1)) {
+    vkGetPhysicalDeviceFormatProperties(physical_device, format,
+                                        &format_properties);
+    if (format_properties.linearTilingFeatures ||
+        format_properties.optimalTilingFeatures ||
+        format_properties.bufferFeatures) {
+      device.formats.insert(std::make_pair(format, format_properties));
+    }
+  }
+
+  if (device.properties.apiVersion >= VK_API_VERSION_1_1) {
+    for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM;
+         // TODO(http://b/171403054): avoid hard-coding last value in the
+         // contiguous range
+         format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
+         format = static_cast<VkFormat>(format + 1)) {
+      vkGetPhysicalDeviceFormatProperties(physical_device, format,
+                                          &format_properties);
+      if (format_properties.linearTilingFeatures ||
+          format_properties.optimalTilingFeatures ||
+          format_properties.bufferFeatures) {
+        device.formats.insert(std::make_pair(format, format_properties));
+      }
+    }\n\n""")
+
+    # Vulkan version data for VK_VERSION_1_1
+    vk_version_data = VK.VULKAN_VERSIONS_AND_STRUCTS_MAPPING["VK_VERSION_1_1"]
+    f.write(generate_vk_version_structs_initialization(vk_version_data, "Properties", "properties") + "\n")
+
+    f.write("""\
+    vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n""")
+
+    features_initialization_code = generate_vk_version_structs_initialization(vk_version_data, "Features", "features")
+    f.write(features_initialization_code)
+
+    f.write("""\
+
+    vkGetPhysicalDeviceFeatures2(physical_device, &features);
+
+    VkPhysicalDeviceExternalFenceInfo external_fence_info = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
+        VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
+    VkExternalFenceProperties external_fence_properties = {};
+
+    for (VkExternalFenceHandleTypeFlagBits handle_type =
+             VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
+         handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+         handle_type =
+             static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) {
+      external_fence_info.handleType = handle_type;
+      vkGetPhysicalDeviceExternalFenceProperties(
+          physical_device, &external_fence_info, &external_fence_properties);
+      if (external_fence_properties.exportFromImportedHandleTypes ||
+          external_fence_properties.compatibleHandleTypes ||
+          external_fence_properties.externalFenceFeatures) {
+        device.external_fence_properties.insert(
+            std::make_pair(handle_type, external_fence_properties));
+      }
+    }
+
+    VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
+        VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
+    VkExternalSemaphoreProperties external_semaphore_properties = {};
+
+    for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
+             VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+         handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+         handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
+             handle_type << 1)) {
+      external_semaphore_info.handleType = handle_type;
+      vkGetPhysicalDeviceExternalSemaphoreProperties(
+          physical_device, &external_semaphore_info,
+          &external_semaphore_properties);
+      if (external_semaphore_properties.exportFromImportedHandleTypes ||
+          external_semaphore_properties.compatibleHandleTypes ||
+          external_semaphore_properties.externalSemaphoreFeatures) {
+        device.external_semaphore_properties.insert(
+            std::make_pair(handle_type, external_semaphore_properties));
+      }
+    }
+  }
+
+  if (device.properties.apiVersion >= VK_API_VERSION_1_2) {\n""")
+
+    cc_code_properties_11, cc_code_features_11 = generate_vk_core_structs_init_code("Core11")
+    cc_code_properties_12, cc_code_features_12 = generate_vk_core_structs_init_code("Core12")
+    cc_code_properties_13, cc_code_features_13 = generate_vk_core_structs_init_code("Core13")
+    cc_code_properties_14, cc_code_features_14 = generate_vk_core_structs_init_code("Core14")
+
+    f.write(cc_code_properties_11)
+    f.write(cc_code_properties_12)
+    f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n")
+    f.write(cc_code_features_11)
+    f.write(cc_code_features_12)
+    f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n")
+    f.write("""\
+  }
+
+  if (device.properties.apiVersion >= VK_API_VERSION_1_3) {\n""")
+    f.write(cc_code_properties_13)
+    f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n")
+    f.write(cc_code_features_13)
+    f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n")
+    f.write("""\
+  }
+
+  if (device.properties.apiVersion >= VK_API_VERSION_1_4) {\n""")
+    f.write(cc_code_properties_14)
+    f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n")
+    f.write(cc_code_features_14)
+    f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n")
+    f.write("""\
+  }
+
+  return device;
+}
+
+VkJsonInstance VkJsonGetInstance() {
+  VkJsonInstance instance;
+  VkResult result;
+  uint32_t count;
+
+  count = 0;
+  result = vkEnumerateInstanceLayerProperties(&count, nullptr);
+  if (result != VK_SUCCESS)
+    return VkJsonInstance();
+  if (count > 0) {
+    std::vector<VkLayerProperties> layers(count);
+    result = vkEnumerateInstanceLayerProperties(&count, layers.data());
+    if (result != VK_SUCCESS)
+      return VkJsonInstance();
+    instance.layers.reserve(count);
+    for (auto& layer : layers) {
+      instance.layers.push_back(VkJsonLayer{layer, std::vector<VkExtensionProperties>()});
+      if (!EnumerateExtensions(layer.layerName,
+                               &instance.layers.back().extensions))
+        return VkJsonInstance();
+    }
+  }
+
+  if (!EnumerateExtensions(nullptr, &instance.extensions))
+    return VkJsonInstance();
+
+  const VkApplicationInfo app_info = {
+      VK_STRUCTURE_TYPE_APPLICATION_INFO,
+      nullptr,
+      "vkjson_info",
+      1,
+      "",
+      0,
+      VK_API_VERSION_1_1,
+  };
+  VkInstanceCreateInfo instance_info = {
+      VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+      nullptr,
+      0,
+      &app_info,
+      0,
+      nullptr,
+      0,
+      nullptr,
+  };
+  VkInstance vkinstance;
+  result = vkCreateInstance(&instance_info, nullptr, &vkinstance);
+  if (result != VK_SUCCESS)
+    return VkJsonInstance();
+
+  count = 0;
+  result = vkEnumeratePhysicalDevices(vkinstance, &count, nullptr);
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+
+  std::vector<VkPhysicalDevice> devices(count, VK_NULL_HANDLE);
+  result = vkEnumeratePhysicalDevices(vkinstance, &count, devices.data());
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+
+  std::map<VkPhysicalDevice, uint32_t> device_map;
+  const uint32_t sz = devices.size();
+  instance.devices.reserve(sz);
+  for (uint32_t i = 0; i < sz; ++i) {
+    device_map.insert(std::make_pair(devices[i], i));
+    instance.devices.emplace_back(VkJsonGetDevice(devices[i]));
+  }
+
+  result = vkEnumerateInstanceVersion(&instance.api_version);
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+
+  count = 0;
+  result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr);
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+
+  VkJsonDeviceGroup device_group;
+  std::vector<VkPhysicalDeviceGroupProperties> group_properties;
+  group_properties.resize(count);
+  for (auto& properties : group_properties) {
+    properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
+    properties.pNext = nullptr;
+  }
+  result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
+                                           group_properties.data());
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+  for (auto properties : group_properties) {
+    device_group.properties = properties;
+    for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
+      device_group.device_inds.push_back(
+          device_map[properties.physicalDevices[i]]);
+    }
+    instance.device_groups.push_back(device_group);
+  }
+
+  vkDestroyInstance(vkinstance, nullptr);
+  return instance;
+}
+
+\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
\ No newline at end of file
diff --git a/vulkan/tests/Android.bp b/vulkan/tests/Android.bp
new file mode 100644
index 0000000..db218c1
--- /dev/null
+++ b/vulkan/tests/Android.bp
@@ -0,0 +1,57 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "libvulkan_test",
+    test_suites: ["general-tests"],
+
+    srcs: [
+        "libvulkan_test.cpp",
+    ],
+
+    strip: {
+        none: true,
+    },
+
+    cflags: [
+        "-DVK_USE_PLATFORM_ANDROID_KHR",
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "hwvulkan_headers",
+        "libvulkanprivate_headers-testing",
+        "vulkan_headers",
+    ],
+
+    cppflags: [
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-c99-extensions",
+        "-Wno-exit-time-destructors",
+        "-Wno-float-equal",
+        "-Wno-global-constructors",
+        "-Wno-zero-length-array",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libgraphicsenv",
+        "liblog",
+        "libmediandk",
+        "libvulkan",
+    ],
+
+    static_libs: [
+        "libgmock",
+        "libgtest",
+        "liblog",
+    ],
+
+}
diff --git a/vulkan/tests/README.md b/vulkan/tests/README.md
new file mode 100644
index 0000000..3c9b66c
--- /dev/null
+++ b/vulkan/tests/README.md
@@ -0,0 +1,24 @@
+#libvulkan_test
+
+This binary contains the unit tests for testing libvulkan (The Vulkan Loader).
+
+These tests rely on the underlying GPU driver to be able to successfully create a valid
+swapchain. These tests are design to run on an Android emulator to give us a consistent GPU
+driver to test against. YMMV when running this on a physical device with an arbitrary GPU
+driver.
+
+To run these tests run:
+```
+atest libvulkan_test
+```
+
+If using an acloud device the full command list for the root of a freshly cloned repo would be:
+```
+source build/envsetup.sh
+lunch aosp_cf_x86_64_phone-trunk_staging-eng
+m
+acloud create --local-image
+atest libvulkan_test
+```
+
+
diff --git a/vulkan/tests/libvulkan_test.cpp b/vulkan/tests/libvulkan_test.cpp
new file mode 100644
index 0000000..7e4bfd8
--- /dev/null
+++ b/vulkan/tests/libvulkan_test.cpp
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2011 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 <android/log.h>
+#include <driver.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <media/NdkImageReader.h>
+#include <vulkan/vulkan.h>
+
+#define LOGI(...) \
+    __android_log_print(ANDROID_LOG_INFO, "libvulkan_test", __VA_ARGS__)
+#define LOGE(...) \
+    __android_log_print(ANDROID_LOG_ERROR, "libvulkan_test", __VA_ARGS__)
+
+#define VK_CHECK(result) ASSERT_EQ(VK_SUCCESS, result)
+
+namespace android {
+
+namespace libvulkantest {
+
+class AImageReaderVulkanSwapchainTest : public ::testing::Test {
+   public:
+    AImageReaderVulkanSwapchainTest() {}
+
+    AImageReader* mReader = nullptr;
+    ANativeWindow* mWindow = nullptr;
+    VkInstance mVkInstance = VK_NULL_HANDLE;
+    VkPhysicalDevice mPhysicalDev = VK_NULL_HANDLE;
+    VkDevice mDevice = VK_NULL_HANDLE;
+    VkSurfaceKHR mSurface = VK_NULL_HANDLE;
+    VkQueue mPresentQueue = VK_NULL_HANDLE;
+    uint32_t mPresentQueueFamily = UINT32_MAX;
+    VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
+
+    void SetUp() override {}
+
+    void TearDown() override {}
+
+    // ------------------------------------------------------
+    // Helper methods
+    // ------------------------------------------------------
+
+    void createVulkanInstance(std::vector<const char*>& layers) {
+        const char* extensions[] = {
+            VK_KHR_SURFACE_EXTENSION_NAME,
+            VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+            VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
+            VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+        };
+
+        VkApplicationInfo appInfo{};
+        appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+        appInfo.pApplicationName = "AImageReader Vulkan Swapchain Test";
+        appInfo.applicationVersion = 1;
+        appInfo.pEngineName = "TestEngine";
+        appInfo.engineVersion = 1;
+        appInfo.apiVersion = VK_API_VERSION_1_0;
+
+        VkInstanceCreateInfo instInfo{};
+        instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+        instInfo.pApplicationInfo = &appInfo;
+        instInfo.enabledExtensionCount =
+            sizeof(extensions) / sizeof(extensions[0]);
+        instInfo.ppEnabledExtensionNames = extensions;
+        instInfo.enabledLayerCount = layers.size();
+        instInfo.ppEnabledLayerNames = layers.data();
+        VkResult res = vkCreateInstance(&instInfo, nullptr, &mVkInstance);
+        VK_CHECK(res);
+        LOGE("Vulkan instance created");
+    }
+
+    void createAImageReader(int width, int height, int format, int maxImages) {
+        media_status_t status =
+            AImageReader_new(width, height, format, maxImages, &mReader);
+        ASSERT_EQ(AMEDIA_OK, status) << "Failed to create AImageReader";
+        ASSERT_NE(nullptr, mReader) << "AImageReader is null";
+
+        // Optionally set a listener
+        AImageReader_ImageListener listener{};
+        listener.context = this;
+        listener.onImageAvailable =
+            &AImageReaderVulkanSwapchainTest::onImageAvailable;
+        AImageReader_setImageListener(mReader, &listener);
+
+        LOGI("AImageReader created with %dx%d, format=%d", width, height,
+             format);
+    }
+
+    void getANativeWindowFromReader() {
+        ASSERT_NE(nullptr, mReader);
+
+        media_status_t status = AImageReader_getWindow(mReader, &mWindow);
+        ASSERT_EQ(AMEDIA_OK, status)
+            << "Failed to get ANativeWindow from AImageReader";
+        ASSERT_NE(nullptr, mWindow) << "ANativeWindow is null";
+        LOGI("ANativeWindow obtained from AImageReader");
+    }
+
+    void createVulkanSurface() {
+        ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+        ASSERT_NE((ANativeWindow*)nullptr, mWindow);
+
+        VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo{};
+        surfaceCreateInfo.sType =
+            VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+        surfaceCreateInfo.window = mWindow;
+
+        VkResult res = vkCreateAndroidSurfaceKHR(
+            mVkInstance, &surfaceCreateInfo, nullptr, &mSurface);
+        VK_CHECK(res);
+        LOGI("Vulkan surface created from ANativeWindow");
+    }
+
+    void pickPhysicalDeviceAndQueueFamily() {
+        ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+
+        uint32_t deviceCount = 0;
+        vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, nullptr);
+        ASSERT_GT(deviceCount, 0U) << "No Vulkan physical devices found!";
+
+        std::vector<VkPhysicalDevice> devices(deviceCount);
+        vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, devices.data());
+
+        for (auto& dev : devices) {
+            uint32_t queueFamilyCount = 0;
+            vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+                                                     nullptr);
+            std::vector<VkQueueFamilyProperties> queueProps(queueFamilyCount);
+            vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+                                                     queueProps.data());
+
+            for (uint32_t i = 0; i < queueFamilyCount; i++) {
+                VkBool32 support = VK_FALSE;
+                vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, mSurface,
+                                                     &support);
+                if (support == VK_TRUE) {
+                    // Found a queue family that can present
+                    mPhysicalDev = dev;
+                    mPresentQueueFamily = i;
+
+                    LOGI(
+                        "Physical device found with queue family %u supporting "
+                        "present",
+                        i);
+                    return;
+                }
+            }
+        }
+
+        FAIL()
+            << "No physical device found that supports present to the surface!";
+    }
+
+    void createDeviceAndGetQueue(std::vector<const char*>& layers,
+                                 std::vector<const char*> inExtensions = {}) {
+        ASSERT_NE((void*)VK_NULL_HANDLE, mPhysicalDev);
+        ASSERT_NE(UINT32_MAX, mPresentQueueFamily);
+
+        float queuePriority = 1.0f;
+        VkDeviceQueueCreateInfo queueInfo{};
+        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queueInfo.queueFamilyIndex = mPresentQueueFamily;
+        queueInfo.queueCount = 1;
+        queueInfo.pQueuePriorities = &queuePriority;
+
+        VkDeviceCreateInfo deviceInfo{};
+        deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+        deviceInfo.queueCreateInfoCount = 1;
+        deviceInfo.pQueueCreateInfos = &queueInfo;
+        deviceInfo.enabledLayerCount = layers.size();
+        deviceInfo.ppEnabledLayerNames = layers.data();
+
+        std::vector<const char*> extensions = {
+            VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+        };
+        for (auto extension : inExtensions) {
+            extensions.push_back(extension);
+        }
+        deviceInfo.enabledExtensionCount = extensions.size();
+        deviceInfo.ppEnabledExtensionNames = extensions.data();
+
+        VkResult res =
+            vkCreateDevice(mPhysicalDev, &deviceInfo, nullptr, &mDevice);
+        VK_CHECK(res);
+        LOGI("Logical device created");
+
+        vkGetDeviceQueue(mDevice, mPresentQueueFamily, 0, &mPresentQueue);
+        ASSERT_NE((VkQueue)VK_NULL_HANDLE, mPresentQueue);
+        LOGI("Acquired present-capable queue");
+    }
+
+    void createSwapchain() {
+        ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice);
+        ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface);
+
+        VkSurfaceCapabilitiesKHR surfaceCaps{};
+        VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+            mPhysicalDev, mSurface, &surfaceCaps));
+
+        uint32_t formatCount = 0;
+        vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+                                             &formatCount, nullptr);
+        ASSERT_GT(formatCount, 0U);
+        std::vector<VkSurfaceFormatKHR> formats(formatCount);
+        vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+                                             &formatCount, formats.data());
+
+        VkSurfaceFormatKHR chosenFormat = formats[0];
+        LOGI("Chosen surface format: %d", chosenFormat.format);
+
+        uint32_t presentModeCount = 0;
+        vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDev, mSurface,
+                                                  &presentModeCount, nullptr);
+        ASSERT_GT(presentModeCount, 0U);
+        std::vector<VkPresentModeKHR> presentModes(presentModeCount);
+        vkGetPhysicalDeviceSurfacePresentModesKHR(
+            mPhysicalDev, mSurface, &presentModeCount, presentModes.data());
+
+        VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;
+        for (auto mode : presentModes) {
+            if (mode == VK_PRESENT_MODE_FIFO_KHR) {
+                chosenPresentMode = mode;
+                break;
+            }
+        }
+        LOGI("Chosen present mode: %d", chosenPresentMode);
+
+        VkExtent2D swapchainExtent{};
+        if (surfaceCaps.currentExtent.width == 0xFFFFFFFF) {
+            swapchainExtent.width = 640;   // fallback
+            swapchainExtent.height = 480;  // fallback
+        } else {
+            swapchainExtent = surfaceCaps.currentExtent;
+        }
+        LOGI("Swapchain extent: %d x %d", swapchainExtent.width,
+             swapchainExtent.height);
+
+        uint32_t desiredImageCount = surfaceCaps.minImageCount + 1;
+        if (surfaceCaps.maxImageCount > 0 &&
+            desiredImageCount > surfaceCaps.maxImageCount) {
+            desiredImageCount = surfaceCaps.maxImageCount;
+        }
+
+        VkSwapchainCreateInfoKHR swapchainInfo{};
+        swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+        swapchainInfo.surface = mSurface;
+        swapchainInfo.minImageCount = desiredImageCount;
+        swapchainInfo.imageFormat = chosenFormat.format;
+        swapchainInfo.imageColorSpace = chosenFormat.colorSpace;
+        swapchainInfo.imageExtent = swapchainExtent;
+        swapchainInfo.imageArrayLayers = 1;
+        swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+        swapchainInfo.preTransform = surfaceCaps.currentTransform;
+        swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
+        swapchainInfo.presentMode = chosenPresentMode;
+        swapchainInfo.clipped = VK_TRUE;
+        swapchainInfo.oldSwapchain = VK_NULL_HANDLE;
+
+        uint32_t queueFamilyIndices[] = {mPresentQueueFamily};
+        swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        swapchainInfo.queueFamilyIndexCount = 1;
+        swapchainInfo.pQueueFamilyIndices = queueFamilyIndices;
+
+        VkResult res =
+            vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain);
+        if (res == VK_SUCCESS) {
+            LOGI("Swapchain created successfully");
+
+            uint32_t swapchainImageCount = 0;
+            vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+                                    nullptr);
+            std::vector<VkImage> swapchainImages(swapchainImageCount);
+            vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+                                    swapchainImages.data());
+
+            LOGI("Swapchain has %u images", swapchainImageCount);
+        } else {
+            LOGI("Swapchain creation failed");
+        }
+    }
+
+    // Image available callback (AImageReader)
+    static void onImageAvailable(void*, AImageReader* reader) {
+        LOGI("onImageAvailable callback triggered");
+        AImage* image = nullptr;
+        media_status_t status = AImageReader_acquireLatestImage(reader, &image);
+        if (status != AMEDIA_OK || !image) {
+            LOGE("Failed to acquire latest image");
+            return;
+        }
+        AImage_delete(image);
+        LOGI("Released acquired image");
+    }
+
+    void cleanUpSwapchainForTest() {
+        if (mSwapchain != VK_NULL_HANDLE) {
+            vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr);
+            mSwapchain = VK_NULL_HANDLE;
+        }
+        if (mDevice != VK_NULL_HANDLE) {
+            vkDestroyDevice(mDevice, nullptr);
+            mDevice = VK_NULL_HANDLE;
+        }
+        if (mSurface != VK_NULL_HANDLE) {
+            vkDestroySurfaceKHR(mVkInstance, mSurface, nullptr);
+            mSurface = VK_NULL_HANDLE;
+        }
+        if (mVkInstance != VK_NULL_HANDLE) {
+            vkDestroyInstance(mVkInstance, nullptr);
+            mVkInstance = VK_NULL_HANDLE;
+        }
+        if (mReader) {
+            // AImageReader_delete(mReader);
+            mReader = nullptr;
+        }
+        // Note: The ANativeWindow from AImageReader is implicitly
+        // managed by the reader, so we don't explicitly delete it.
+        mWindow = nullptr;
+    }
+
+    void buildSwapchianForTest(std::vector<const char*>& instanceLayers,
+                               std::vector<const char*>& deviceLayers) {
+        createVulkanInstance(instanceLayers);
+
+        // the "atest libvulkan_test" command will execute this test as a binary
+        // (not apk) on the device. Consequently we can't render to the screen
+        // and need to work around this by using AImageReader*
+        createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+        getANativeWindowFromReader();
+        createVulkanSurface();
+        pickPhysicalDeviceAndQueueFamily();
+
+        createDeviceAndGetQueue(deviceLayers);
+        createSwapchain();
+    }
+};
+
+TEST_F(AImageReaderVulkanSwapchainTest, TestHelperMethods) {
+    // Verify that the basic plumbing/helper functions of these tests is
+    // working. This doesn't directly test any of the layer code. It only
+    // verifies that we can successfully create a swapchain with an AImageReader
+
+    std::vector<const char*> instanceLayers;
+    std::vector<const char*> deviceLayers;
+    buildSwapchianForTest(deviceLayers, instanceLayers);
+
+    ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE);
+    ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE);
+    ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE);
+    ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE);
+    ASSERT_NE(mSwapchain, (VkSwapchainKHR)VK_NULL_HANDLE);
+    cleanUpSwapchainForTest();
+}
+
+// Passing state in these tests requires global state. Wrap each test in an
+// anonymous namespace to prevent conflicting names.
+namespace {
+
+VKAPI_ATTR VkResult VKAPI_CALL hookedGetPhysicalDeviceImageFormatProperties2KHR(
+    VkPhysicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2*,
+    VkImageFormatProperties2*) {
+    return VK_ERROR_SURFACE_LOST_KHR;
+}
+
+static PFN_vkGetSwapchainGrallocUsage2ANDROID
+    pfnNextGetSwapchainGrallocUsage2ANDROID = nullptr;
+
+static bool g_grallocCalled = false;
+
+VKAPI_ATTR VkResult VKAPI_CALL hookGetSwapchainGrallocUsage2ANDROID(
+    VkDevice device,
+    VkFormat format,
+    VkImageUsageFlags imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t* grallocConsumerUsage,
+    uint64_t* grallocProducerUsage) {
+    g_grallocCalled = true;
+    if (pfnNextGetSwapchainGrallocUsage2ANDROID) {
+        return pfnNextGetSwapchainGrallocUsage2ANDROID(
+            device, format, imageUsage, swapchainImageUsage,
+            grallocConsumerUsage, grallocProducerUsage);
+    }
+
+    return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+TEST_F(AImageReaderVulkanSwapchainTest, getProducerUsageFallbackTest1) {
+    // BUG: 379230826
+    // Verify that getProducerUsage falls back to
+    // GetSwapchainGrallocUsage*ANDROID if GPDIFP2 fails
+    std::vector<const char*> instanceLayers = {};
+    std::vector<const char*> deviceLayers = {};
+    createVulkanInstance(instanceLayers);
+
+    createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+    getANativeWindowFromReader();
+    createVulkanSurface();
+    pickPhysicalDeviceAndQueueFamily();
+
+    createDeviceAndGetQueue(deviceLayers);
+    auto& pdev = vulkan::driver::GetData(mDevice).driver_physical_device;
+    auto& pdevDispatchTable = vulkan::driver::GetData(pdev).driver;
+    auto& deviceDispatchTable = vulkan::driver::GetData(mDevice).driver;
+
+    ASSERT_NE(deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID, nullptr);
+
+    pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 =
+        hookedGetPhysicalDeviceImageFormatProperties2KHR;
+    deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID =
+        hookGetSwapchainGrallocUsage2ANDROID;
+
+    ASSERT_FALSE(g_grallocCalled);
+
+    createSwapchain();
+
+    ASSERT_TRUE(g_grallocCalled);
+
+    ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE);
+    ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE);
+    ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE);
+    ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE);
+    cleanUpSwapchainForTest();
+}
+
+}  // namespace
+
+// Passing state in these tests requires global state. Wrap each test in an
+// anonymous namespace to prevent conflicting names.
+namespace {
+
+static bool g_returnNotSupportedOnce = true;
+
+VKAPI_ATTR VkResult VKAPI_CALL
+Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce(
+    VkPhysicalDevice /*physicalDevice*/,
+    const VkPhysicalDeviceImageFormatInfo2* /*pImageFormatInfo*/,
+    VkImageFormatProperties2* /*pImageFormatProperties*/) {
+    if (g_returnNotSupportedOnce) {
+        g_returnNotSupportedOnce = false;
+        return VK_ERROR_FORMAT_NOT_SUPPORTED;
+    }
+    return VK_SUCCESS;
+}
+
+TEST_F(AImageReaderVulkanSwapchainTest, SurfaceFormats2KHR_IgnoreNotSupported) {
+    // BUG: 357903074
+    // Verify that vkGetPhysicalDeviceSurfaceFormats2KHR properly
+    // ignores VK_ERROR_FORMAT_NOT_SUPPORTED and continues enumerating formats.
+    std::vector<const char*> instanceLayers;
+    createVulkanInstance(instanceLayers);
+    createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+    getANativeWindowFromReader();
+    createVulkanSurface();
+    pickPhysicalDeviceAndQueueFamily();
+
+    auto& pdevDispatchTable = vulkan::driver::GetData(mPhysicalDev).driver;
+    pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 =
+        Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce;
+
+    PFN_vkGetPhysicalDeviceSurfaceFormats2KHR
+        pfnGetPhysicalDeviceSurfaceFormats2KHR =
+            reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormats2KHR>(
+                vkGetInstanceProcAddr(mVkInstance,
+                                      "vkGetPhysicalDeviceSurfaceFormats2KHR"));
+    ASSERT_NE(nullptr, pfnGetPhysicalDeviceSurfaceFormats2KHR)
+        << "Could not get pointer to vkGetPhysicalDeviceSurfaceFormats2KHR";
+
+    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2{};
+    surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
+    surfaceInfo2.pNext = nullptr;
+    surfaceInfo2.surface = mSurface;
+
+    uint32_t formatCount = 0;
+    VkResult res = pfnGetPhysicalDeviceSurfaceFormats2KHR(
+        mPhysicalDev, &surfaceInfo2, &formatCount, nullptr);
+
+    // If the loader never tries a second format, it might fail or 0-out the
+    // formatCount. The patch ensures it continues to the next format rather
+    // than bailing out on the first NOT_SUPPORTED.
+    ASSERT_EQ(VK_SUCCESS, res)
+        << "vkGetPhysicalDeviceSurfaceFormats2KHR failed unexpectedly";
+    ASSERT_GT(formatCount, 0U)
+        << "No surface formats found; the loader may have bailed early.";
+
+    std::vector<VkSurfaceFormat2KHR> formats(formatCount);
+    for (auto& f : formats) {
+        f.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR;
+        f.pNext = nullptr;
+    }
+    res = pfnGetPhysicalDeviceSurfaceFormats2KHR(mPhysicalDev, &surfaceInfo2,
+                                                 &formatCount, formats.data());
+    ASSERT_EQ(VK_SUCCESS, res) << "Failed to retrieve surface formats";
+
+    LOGI(
+        "SurfaceFormats2KHR_IgnoreNotSupported test: found %u formats after "
+        "ignoring NOT_SUPPORTED",
+        formatCount);
+
+    cleanUpSwapchainForTest();
+}
+
+}  // namespace
+
+namespace {
+
+TEST_F(AImageReaderVulkanSwapchainTest, MutableFormatSwapchainTest) {
+    // Test swapchain with mutable format extension
+    std::vector<const char*> instanceLayers;
+    std::vector<const char*> deviceLayers;
+    std::vector<const char*> deviceExtensions = {
+        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+        VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
+        VK_KHR_MAINTENANCE2_EXTENSION_NAME,
+        VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME};
+
+    createVulkanInstance(instanceLayers);
+    createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+    getANativeWindowFromReader();
+    createVulkanSurface();
+    pickPhysicalDeviceAndQueueFamily();
+    createDeviceAndGetQueue(deviceLayers, deviceExtensions);
+
+    ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice);
+    ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface);
+
+    VkSurfaceCapabilitiesKHR surfaceCaps{};
+    VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDev, mSurface,
+                                                       &surfaceCaps));
+
+    uint32_t formatCount = 0;
+    vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount,
+                                         nullptr);
+    ASSERT_GT(formatCount, 0U);
+    std::vector<VkSurfaceFormatKHR> formats(formatCount);
+    vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount,
+                                         formats.data());
+
+    VkFormat viewFormats[2] = {formats[0].format, formats[0].format};
+    if (formatCount > 1) {
+        viewFormats[1] = formats[1].format;
+    }
+
+    VkImageFormatListCreateInfoKHR formatList{};
+    formatList.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR;
+    formatList.viewFormatCount = 2;
+    formatList.pViewFormats = viewFormats;
+
+    VkSwapchainCreateInfoKHR swapchainInfo{};
+    swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    swapchainInfo.pNext = &formatList;
+    swapchainInfo.surface = mSurface;
+    swapchainInfo.minImageCount = surfaceCaps.minImageCount + 1;
+    swapchainInfo.imageFormat = formats[0].format;
+    swapchainInfo.imageColorSpace = formats[0].colorSpace;
+    swapchainInfo.imageExtent = surfaceCaps.currentExtent;
+    swapchainInfo.imageArrayLayers = 1;
+    swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    swapchainInfo.preTransform = surfaceCaps.currentTransform;
+    swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
+    swapchainInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
+    swapchainInfo.clipped = VK_TRUE;
+
+    swapchainInfo.flags = VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
+
+    uint32_t queueFamilyIndices[] = {mPresentQueueFamily};
+    swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    swapchainInfo.queueFamilyIndexCount = 1;
+    swapchainInfo.pQueueFamilyIndices = queueFamilyIndices;
+
+    VkResult res =
+        vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain);
+    if (res == VK_SUCCESS) {
+        LOGI("Mutable format swapchain created successfully");
+
+        uint32_t imageCount = 0;
+        vkGetSwapchainImagesKHR(mDevice, mSwapchain, &imageCount, nullptr);
+        ASSERT_GT(imageCount, 0U);
+    } else {
+        LOGI(
+            "Mutable format swapchain creation failed (extension may not be "
+            "supported)");
+    }
+
+    cleanUpSwapchainForTest();
+}
+
+}  // namespace
+
+}  // namespace libvulkantest
+
+}  // namespace android
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 8c0cce2..531a623 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -56,13 +56,15 @@
 // Floating point fields of Vulkan structure use single precision. The string
 // output of max double value in c++ will be larger than Java double's infinity
 // value. Below fake double max/min values are only to serve the safe json text
-// parsing in between C++ and Java, becasue Java json library simply cannot
+// parsing in between C++ and Java, because Java json library simply cannot
 // handle infinity.
 static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max();
 static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX;
 
-template <typename T> struct EnumTraits;
-template <> struct EnumTraits<VkPhysicalDeviceType> {
+template <typename T>
+struct EnumTraits;
+template <>
+struct EnumTraits<VkPhysicalDeviceType> {
   static bool exist(uint32_t e) {
     switch (e) {
       case VK_PHYSICAL_DEVICE_TYPE_OTHER:
@@ -76,7 +78,8 @@
   }
 };
 
-template <> struct EnumTraits<VkFormat> {
+template <>
+struct EnumTraits<VkFormat> {
   static bool exist(uint32_t e) {
     switch (e) {
       case VK_FORMAT_UNDEFINED:
@@ -482,495 +485,13 @@
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkExtent3D* extents) {
-  return
-    visitor->Visit("width", &extents->width) &&
-    visitor->Visit("height", &extents->height) &&
-    visitor->Visit("depth", &extents->depth);
+  return visitor->Visit("width", &extents->width) &&
+         visitor->Visit("height", &extents->height) &&
+         visitor->Visit("depth", &extents->depth);
 }
 
 template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkImageFormatProperties* properties) {
-  return
-    visitor->Visit("maxExtent", &properties->maxExtent) &&
-    visitor->Visit("maxMipLevels", &properties->maxMipLevels) &&
-    visitor->Visit("maxArrayLayers", &properties->maxArrayLayers) &&
-    visitor->Visit("sampleCounts", &properties->sampleCounts) &&
-    visitor->Visit("maxResourceSize", &properties->maxResourceSize);
-}
-
-// clang-format off
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) {
-  return
-    visitor->Visit("maxImageDimension1D", &limits->maxImageDimension1D) &&
-    visitor->Visit("maxImageDimension2D", &limits->maxImageDimension2D) &&
-    visitor->Visit("maxImageDimension3D", &limits->maxImageDimension3D) &&
-    visitor->Visit("maxImageDimensionCube", &limits->maxImageDimensionCube) &&
-    visitor->Visit("maxImageArrayLayers", &limits->maxImageArrayLayers) &&
-    visitor->Visit("maxTexelBufferElements", &limits->maxTexelBufferElements) &&
-    visitor->Visit("maxUniformBufferRange", &limits->maxUniformBufferRange) &&
-    visitor->Visit("maxStorageBufferRange", &limits->maxStorageBufferRange) &&
-    visitor->Visit("maxPushConstantsSize", &limits->maxPushConstantsSize) &&
-    visitor->Visit("maxMemoryAllocationCount", &limits->maxMemoryAllocationCount) &&
-    visitor->Visit("maxSamplerAllocationCount", &limits->maxSamplerAllocationCount) &&
-    visitor->Visit("bufferImageGranularity", &limits->bufferImageGranularity) &&
-    visitor->Visit("sparseAddressSpaceSize", &limits->sparseAddressSpaceSize) &&
-    visitor->Visit("maxBoundDescriptorSets", &limits->maxBoundDescriptorSets) &&
-    visitor->Visit("maxPerStageDescriptorSamplers", &limits->maxPerStageDescriptorSamplers) &&
-    visitor->Visit("maxPerStageDescriptorUniformBuffers", &limits->maxPerStageDescriptorUniformBuffers) &&
-    visitor->Visit("maxPerStageDescriptorStorageBuffers", &limits->maxPerStageDescriptorStorageBuffers) &&
-    visitor->Visit("maxPerStageDescriptorSampledImages", &limits->maxPerStageDescriptorSampledImages) &&
-    visitor->Visit("maxPerStageDescriptorStorageImages", &limits->maxPerStageDescriptorStorageImages) &&
-    visitor->Visit("maxPerStageDescriptorInputAttachments", &limits->maxPerStageDescriptorInputAttachments) &&
-    visitor->Visit("maxPerStageResources", &limits->maxPerStageResources) &&
-    visitor->Visit("maxDescriptorSetSamplers", &limits->maxDescriptorSetSamplers) &&
-    visitor->Visit("maxDescriptorSetUniformBuffers", &limits->maxDescriptorSetUniformBuffers) &&
-    visitor->Visit("maxDescriptorSetUniformBuffersDynamic", &limits->maxDescriptorSetUniformBuffersDynamic) &&
-    visitor->Visit("maxDescriptorSetStorageBuffers", &limits->maxDescriptorSetStorageBuffers) &&
-    visitor->Visit("maxDescriptorSetStorageBuffersDynamic", &limits->maxDescriptorSetStorageBuffersDynamic) &&
-    visitor->Visit("maxDescriptorSetSampledImages", &limits->maxDescriptorSetSampledImages) &&
-    visitor->Visit("maxDescriptorSetStorageImages", &limits->maxDescriptorSetStorageImages) &&
-    visitor->Visit("maxDescriptorSetInputAttachments", &limits->maxDescriptorSetInputAttachments) &&
-    visitor->Visit("maxVertexInputAttributes", &limits->maxVertexInputAttributes) &&
-    visitor->Visit("maxVertexInputBindings", &limits->maxVertexInputBindings) &&
-    visitor->Visit("maxVertexInputAttributeOffset", &limits->maxVertexInputAttributeOffset) &&
-    visitor->Visit("maxVertexInputBindingStride", &limits->maxVertexInputBindingStride) &&
-    visitor->Visit("maxVertexOutputComponents", &limits->maxVertexOutputComponents) &&
-    visitor->Visit("maxTessellationGenerationLevel", &limits->maxTessellationGenerationLevel) &&
-    visitor->Visit("maxTessellationPatchSize", &limits->maxTessellationPatchSize) &&
-    visitor->Visit("maxTessellationControlPerVertexInputComponents", &limits->maxTessellationControlPerVertexInputComponents) &&
-    visitor->Visit("maxTessellationControlPerVertexOutputComponents", &limits->maxTessellationControlPerVertexOutputComponents) &&
-    visitor->Visit("maxTessellationControlPerPatchOutputComponents", &limits->maxTessellationControlPerPatchOutputComponents) &&
-    visitor->Visit("maxTessellationControlTotalOutputComponents", &limits->maxTessellationControlTotalOutputComponents) &&
-    visitor->Visit("maxTessellationEvaluationInputComponents", &limits->maxTessellationEvaluationInputComponents) &&
-    visitor->Visit("maxTessellationEvaluationOutputComponents", &limits->maxTessellationEvaluationOutputComponents) &&
-    visitor->Visit("maxGeometryShaderInvocations", &limits->maxGeometryShaderInvocations) &&
-    visitor->Visit("maxGeometryInputComponents", &limits->maxGeometryInputComponents) &&
-    visitor->Visit("maxGeometryOutputComponents", &limits->maxGeometryOutputComponents) &&
-    visitor->Visit("maxGeometryOutputVertices", &limits->maxGeometryOutputVertices) &&
-    visitor->Visit("maxGeometryTotalOutputComponents", &limits->maxGeometryTotalOutputComponents) &&
-    visitor->Visit("maxFragmentInputComponents", &limits->maxFragmentInputComponents) &&
-    visitor->Visit("maxFragmentOutputAttachments", &limits->maxFragmentOutputAttachments) &&
-    visitor->Visit("maxFragmentDualSrcAttachments", &limits->maxFragmentDualSrcAttachments) &&
-    visitor->Visit("maxFragmentCombinedOutputResources", &limits->maxFragmentCombinedOutputResources) &&
-    visitor->Visit("maxComputeSharedMemorySize", &limits->maxComputeSharedMemorySize) &&
-    visitor->Visit("maxComputeWorkGroupCount", &limits->maxComputeWorkGroupCount) &&
-    visitor->Visit("maxComputeWorkGroupInvocations", &limits->maxComputeWorkGroupInvocations) &&
-    visitor->Visit("maxComputeWorkGroupSize", &limits->maxComputeWorkGroupSize) &&
-    visitor->Visit("subPixelPrecisionBits", &limits->subPixelPrecisionBits) &&
-    visitor->Visit("subTexelPrecisionBits", &limits->subTexelPrecisionBits) &&
-    visitor->Visit("mipmapPrecisionBits", &limits->mipmapPrecisionBits) &&
-    visitor->Visit("maxDrawIndexedIndexValue", &limits->maxDrawIndexedIndexValue) &&
-    visitor->Visit("maxDrawIndirectCount", &limits->maxDrawIndirectCount) &&
-    visitor->Visit("maxSamplerLodBias", &limits->maxSamplerLodBias) &&
-    visitor->Visit("maxSamplerAnisotropy", &limits->maxSamplerAnisotropy) &&
-    visitor->Visit("maxViewports", &limits->maxViewports) &&
-    visitor->Visit("maxViewportDimensions", &limits->maxViewportDimensions) &&
-    visitor->Visit("viewportBoundsRange", &limits->viewportBoundsRange) &&
-    visitor->Visit("viewportSubPixelBits", &limits->viewportSubPixelBits) &&
-    visitor->Visit("minMemoryMapAlignment", &limits->minMemoryMapAlignment) &&
-    visitor->Visit("minTexelBufferOffsetAlignment", &limits->minTexelBufferOffsetAlignment) &&
-    visitor->Visit("minUniformBufferOffsetAlignment", &limits->minUniformBufferOffsetAlignment) &&
-    visitor->Visit("minStorageBufferOffsetAlignment", &limits->minStorageBufferOffsetAlignment) &&
-    visitor->Visit("minTexelOffset", &limits->minTexelOffset) &&
-    visitor->Visit("maxTexelOffset", &limits->maxTexelOffset) &&
-    visitor->Visit("minTexelGatherOffset", &limits->minTexelGatherOffset) &&
-    visitor->Visit("maxTexelGatherOffset", &limits->maxTexelGatherOffset) &&
-    visitor->Visit("minInterpolationOffset", &limits->minInterpolationOffset) &&
-    visitor->Visit("maxInterpolationOffset", &limits->maxInterpolationOffset) &&
-    visitor->Visit("subPixelInterpolationOffsetBits", &limits->subPixelInterpolationOffsetBits) &&
-    visitor->Visit("maxFramebufferWidth", &limits->maxFramebufferWidth) &&
-    visitor->Visit("maxFramebufferHeight", &limits->maxFramebufferHeight) &&
-    visitor->Visit("maxFramebufferLayers", &limits->maxFramebufferLayers) &&
-    visitor->Visit("framebufferColorSampleCounts", &limits->framebufferColorSampleCounts) &&
-    visitor->Visit("framebufferDepthSampleCounts", &limits->framebufferDepthSampleCounts) &&
-    visitor->Visit("framebufferStencilSampleCounts", &limits->framebufferStencilSampleCounts) &&
-    visitor->Visit("framebufferNoAttachmentsSampleCounts", &limits->framebufferNoAttachmentsSampleCounts) &&
-    visitor->Visit("maxColorAttachments", &limits->maxColorAttachments) &&
-    visitor->Visit("sampledImageColorSampleCounts", &limits->sampledImageColorSampleCounts) &&
-    visitor->Visit("sampledImageIntegerSampleCounts", &limits->sampledImageIntegerSampleCounts) &&
-    visitor->Visit("sampledImageDepthSampleCounts", &limits->sampledImageDepthSampleCounts) &&
-    visitor->Visit("sampledImageStencilSampleCounts", &limits->sampledImageStencilSampleCounts) &&
-    visitor->Visit("storageImageSampleCounts", &limits->storageImageSampleCounts) &&
-    visitor->Visit("maxSampleMaskWords", &limits->maxSampleMaskWords) &&
-    visitor->Visit("timestampComputeAndGraphics", &limits->timestampComputeAndGraphics) &&
-    visitor->Visit("timestampPeriod", &limits->timestampPeriod) &&
-    visitor->Visit("maxClipDistances", &limits->maxClipDistances) &&
-    visitor->Visit("maxCullDistances", &limits->maxCullDistances) &&
-    visitor->Visit("maxCombinedClipAndCullDistances", &limits->maxCombinedClipAndCullDistances) &&
-    visitor->Visit("discreteQueuePriorities", &limits->discreteQueuePriorities) &&
-    visitor->Visit("pointSizeRange", &limits->pointSizeRange) &&
-    visitor->Visit("lineWidthRange", &limits->lineWidthRange) &&
-    visitor->Visit("pointSizeGranularity", &limits->pointSizeGranularity) &&
-    visitor->Visit("lineWidthGranularity", &limits->lineWidthGranularity) &&
-    visitor->Visit("strictLines", &limits->strictLines) &&
-    visitor->Visit("standardSampleLocations", &limits->standardSampleLocations) &&
-    visitor->Visit("optimalBufferCopyOffsetAlignment", &limits->optimalBufferCopyOffsetAlignment) &&
-    visitor->Visit("optimalBufferCopyRowPitchAlignment", &limits->optimalBufferCopyRowPitchAlignment) &&
-    visitor->Visit("nonCoherentAtomSize", &limits->nonCoherentAtomSize);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkPhysicalDeviceSparseProperties* properties) {
-  return
-    visitor->Visit("residencyStandard2DBlockShape", &properties->residencyStandard2DBlockShape) &&
-    visitor->Visit("residencyStandard2DMultisampleBlockShape", &properties->residencyStandard2DMultisampleBlockShape) &&
-    visitor->Visit("residencyStandard3DBlockShape", &properties->residencyStandard3DBlockShape) &&
-    visitor->Visit("residencyAlignedMipSize", &properties->residencyAlignedMipSize) &&
-    visitor->Visit("residencyNonResidentStrict", &properties->residencyNonResidentStrict);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkPhysicalDeviceProperties* properties) {
-  return
-    visitor->Visit("apiVersion", &properties->apiVersion) &&
-    visitor->Visit("driverVersion", &properties->driverVersion) &&
-    visitor->Visit("vendorID", &properties->vendorID) &&
-    visitor->Visit("deviceID", &properties->deviceID) &&
-    visitor->Visit("deviceType", &properties->deviceType) &&
-    visitor->Visit("deviceName", &properties->deviceName) &&
-    visitor->Visit("pipelineCacheUUID", &properties->pipelineCacheUUID) &&
-    visitor->Visit("limits", &properties->limits) &&
-    visitor->Visit("sparseProperties", &properties->sparseProperties);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceFeatures* features) {
-  return
-    visitor->Visit("robustBufferAccess", &features->robustBufferAccess) &&
-    visitor->Visit("fullDrawIndexUint32", &features->fullDrawIndexUint32) &&
-    visitor->Visit("imageCubeArray", &features->imageCubeArray) &&
-    visitor->Visit("independentBlend", &features->independentBlend) &&
-    visitor->Visit("geometryShader", &features->geometryShader) &&
-    visitor->Visit("tessellationShader", &features->tessellationShader) &&
-    visitor->Visit("sampleRateShading", &features->sampleRateShading) &&
-    visitor->Visit("dualSrcBlend", &features->dualSrcBlend) &&
-    visitor->Visit("logicOp", &features->logicOp) &&
-    visitor->Visit("multiDrawIndirect", &features->multiDrawIndirect) &&
-    visitor->Visit("drawIndirectFirstInstance", &features->drawIndirectFirstInstance) &&
-    visitor->Visit("depthClamp", &features->depthClamp) &&
-    visitor->Visit("depthBiasClamp", &features->depthBiasClamp) &&
-    visitor->Visit("fillModeNonSolid", &features->fillModeNonSolid) &&
-    visitor->Visit("depthBounds", &features->depthBounds) &&
-    visitor->Visit("wideLines", &features->wideLines) &&
-    visitor->Visit("largePoints", &features->largePoints) &&
-    visitor->Visit("alphaToOne", &features->alphaToOne) &&
-    visitor->Visit("multiViewport", &features->multiViewport) &&
-    visitor->Visit("samplerAnisotropy", &features->samplerAnisotropy) &&
-    visitor->Visit("textureCompressionETC2", &features->textureCompressionETC2) &&
-    visitor->Visit("textureCompressionASTC_LDR", &features->textureCompressionASTC_LDR) &&
-    visitor->Visit("textureCompressionBC", &features->textureCompressionBC) &&
-    visitor->Visit("occlusionQueryPrecise", &features->occlusionQueryPrecise) &&
-    visitor->Visit("pipelineStatisticsQuery", &features->pipelineStatisticsQuery) &&
-    visitor->Visit("vertexPipelineStoresAndAtomics", &features->vertexPipelineStoresAndAtomics) &&
-    visitor->Visit("fragmentStoresAndAtomics", &features->fragmentStoresAndAtomics) &&
-    visitor->Visit("shaderTessellationAndGeometryPointSize", &features->shaderTessellationAndGeometryPointSize) &&
-    visitor->Visit("shaderImageGatherExtended", &features->shaderImageGatherExtended) &&
-    visitor->Visit("shaderStorageImageExtendedFormats", &features->shaderStorageImageExtendedFormats) &&
-    visitor->Visit("shaderStorageImageMultisample", &features->shaderStorageImageMultisample) &&
-    visitor->Visit("shaderStorageImageReadWithoutFormat", &features->shaderStorageImageReadWithoutFormat) &&
-    visitor->Visit("shaderStorageImageWriteWithoutFormat", &features->shaderStorageImageWriteWithoutFormat) &&
-    visitor->Visit("shaderUniformBufferArrayDynamicIndexing", &features->shaderUniformBufferArrayDynamicIndexing) &&
-    visitor->Visit("shaderSampledImageArrayDynamicIndexing", &features->shaderSampledImageArrayDynamicIndexing) &&
-    visitor->Visit("shaderStorageBufferArrayDynamicIndexing", &features->shaderStorageBufferArrayDynamicIndexing) &&
-    visitor->Visit("shaderStorageImageArrayDynamicIndexing", &features->shaderStorageImageArrayDynamicIndexing) &&
-    visitor->Visit("shaderClipDistance", &features->shaderClipDistance) &&
-    visitor->Visit("shaderCullDistance", &features->shaderCullDistance) &&
-    visitor->Visit("shaderFloat64", &features->shaderFloat64) &&
-    visitor->Visit("shaderInt64", &features->shaderInt64) &&
-    visitor->Visit("shaderInt16", &features->shaderInt16) &&
-    visitor->Visit("shaderResourceResidency", &features->shaderResourceResidency) &&
-    visitor->Visit("shaderResourceMinLod", &features->shaderResourceMinLod) &&
-    visitor->Visit("sparseBinding", &features->sparseBinding) &&
-    visitor->Visit("sparseResidencyBuffer", &features->sparseResidencyBuffer) &&
-    visitor->Visit("sparseResidencyImage2D", &features->sparseResidencyImage2D) &&
-    visitor->Visit("sparseResidencyImage3D", &features->sparseResidencyImage3D) &&
-    visitor->Visit("sparseResidency2Samples", &features->sparseResidency2Samples) &&
-    visitor->Visit("sparseResidency4Samples", &features->sparseResidency4Samples) &&
-    visitor->Visit("sparseResidency8Samples", &features->sparseResidency8Samples) &&
-    visitor->Visit("sparseResidency16Samples", &features->sparseResidency16Samples) &&
-    visitor->Visit("sparseResidencyAliased", &features->sparseResidencyAliased) &&
-    visitor->Visit("variableMultisampleRate", &features->variableMultisampleRate) &&
-    visitor->Visit("inheritedQueries", &features->inheritedQueries);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkJsonCore12* core) {
-  return
-    visitor->Visit("features", &core->features) &&
-    visitor->Visit("properties", &core->properties);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Properties* properties) {
-  return
-    visitor->Visit("driverID", &properties->driverID) &&
-    visitor->Visit("driverName", &properties->driverName) &&
-    visitor->Visit("driverInfo", &properties->driverInfo) &&
-    visitor->Visit("conformanceVersion", &properties->conformanceVersion) &&
-    visitor->Visit("denormBehaviorIndependence", &properties->denormBehaviorIndependence) &&
-    visitor->Visit("roundingModeIndependence", &properties->roundingModeIndependence) &&
-    visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", &properties->shaderSignedZeroInfNanPreserveFloat16) &&
-    visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", &properties->shaderSignedZeroInfNanPreserveFloat32) &&
-    visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", &properties->shaderSignedZeroInfNanPreserveFloat64) &&
-    visitor->Visit("shaderDenormPreserveFloat16", &properties->shaderDenormPreserveFloat16) &&
-    visitor->Visit("shaderDenormPreserveFloat32", &properties->shaderDenormPreserveFloat32) &&
-    visitor->Visit("shaderDenormPreserveFloat64", &properties->shaderDenormPreserveFloat64) &&
-    visitor->Visit("shaderDenormFlushToZeroFloat16", &properties->shaderDenormFlushToZeroFloat16) &&
-    visitor->Visit("shaderDenormFlushToZeroFloat32", &properties->shaderDenormFlushToZeroFloat32) &&
-    visitor->Visit("shaderDenormFlushToZeroFloat64", &properties->shaderDenormFlushToZeroFloat64) &&
-    visitor->Visit("shaderRoundingModeRTEFloat16", &properties->shaderRoundingModeRTEFloat16) &&
-    visitor->Visit("shaderRoundingModeRTEFloat32", &properties->shaderRoundingModeRTEFloat32) &&
-    visitor->Visit("shaderRoundingModeRTEFloat64", &properties->shaderRoundingModeRTEFloat64) &&
-    visitor->Visit("shaderRoundingModeRTZFloat16", &properties->shaderRoundingModeRTZFloat16) &&
-    visitor->Visit("shaderRoundingModeRTZFloat32", &properties->shaderRoundingModeRTZFloat32) &&
-    visitor->Visit("shaderRoundingModeRTZFloat64", &properties->shaderRoundingModeRTZFloat64) &&
-    visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools", &properties->maxUpdateAfterBindDescriptorsInAllPools) &&
-    visitor->Visit("shaderUniformBufferArrayNonUniformIndexingNative", &properties->shaderUniformBufferArrayNonUniformIndexingNative) &&
-    visitor->Visit("shaderSampledImageArrayNonUniformIndexingNative", &properties->shaderSampledImageArrayNonUniformIndexingNative) &&
-    visitor->Visit("shaderStorageBufferArrayNonUniformIndexingNative", &properties->shaderStorageBufferArrayNonUniformIndexingNative) &&
-    visitor->Visit("shaderStorageImageArrayNonUniformIndexingNative", &properties->shaderStorageImageArrayNonUniformIndexingNative) &&
-    visitor->Visit("shaderInputAttachmentArrayNonUniformIndexingNative", &properties->shaderInputAttachmentArrayNonUniformIndexingNative) &&
-    visitor->Visit("robustBufferAccessUpdateAfterBind", &properties->robustBufferAccessUpdateAfterBind) &&
-    visitor->Visit("quadDivergentImplicitLod", &properties->quadDivergentImplicitLod) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindSamplers", &properties->maxPerStageDescriptorUpdateAfterBindSamplers) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindUniformBuffers", &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageBuffers", &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindSampledImages", &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageImages", &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindInputAttachments", &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) &&
-    visitor->Visit("maxPerStageUpdateAfterBindResources", &properties->maxPerStageUpdateAfterBindResources) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers", &properties->maxDescriptorSetUpdateAfterBindSamplers) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffers", &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffers", &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindSampledImages", &properties->maxDescriptorSetUpdateAfterBindSampledImages) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageImages", &properties->maxDescriptorSetUpdateAfterBindStorageImages) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindInputAttachments", &properties->maxDescriptorSetUpdateAfterBindInputAttachments) &&
-    visitor->Visit("supportedDepthResolveModes", &properties->supportedDepthResolveModes) &&
-    visitor->Visit("supportedStencilResolveModes", &properties->supportedStencilResolveModes) &&
-    visitor->Visit("independentResolveNone", &properties->independentResolveNone) &&
-    visitor->Visit("independentResolve", &properties->independentResolve) &&
-    visitor->Visit("filterMinmaxSingleComponentFormats", &properties->filterMinmaxSingleComponentFormats) &&
-    visitor->Visit("filterMinmaxImageComponentMapping", &properties->filterMinmaxImageComponentMapping) &&
-    visitor->Visit("maxTimelineSemaphoreValueDifference", &properties->maxTimelineSemaphoreValueDifference) &&
-    visitor->Visit("framebufferIntegerColorSampleCounts", &properties->framebufferIntegerColorSampleCounts);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Features* features) {
-  return
-    visitor->Visit("samplerMirrorClampToEdge", &features->samplerMirrorClampToEdge) &&
-    visitor->Visit("drawIndirectCount", &features->drawIndirectCount) &&
-    visitor->Visit("storageBuffer8BitAccess", &features->storageBuffer8BitAccess) &&
-    visitor->Visit("uniformAndStorageBuffer8BitAccess", &features->uniformAndStorageBuffer8BitAccess) &&
-    visitor->Visit("storagePushConstant8", &features->storagePushConstant8) &&
-    visitor->Visit("shaderBufferInt64Atomics", &features->shaderBufferInt64Atomics) &&
-    visitor->Visit("shaderSharedInt64Atomics", &features->shaderSharedInt64Atomics) &&
-    visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
-    visitor->Visit("shaderInt8", &features->shaderInt8) &&
-    visitor->Visit("descriptorIndexing", &features->descriptorIndexing) &&
-    visitor->Visit("shaderInputAttachmentArrayDynamicIndexing", &features->shaderInputAttachmentArrayDynamicIndexing) &&
-    visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing", &features->shaderUniformTexelBufferArrayDynamicIndexing) &&
-    visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing", &features->shaderStorageTexelBufferArrayDynamicIndexing) &&
-    visitor->Visit("shaderUniformBufferArrayNonUniformIndexing", &features->shaderUniformBufferArrayNonUniformIndexing) &&
-    visitor->Visit("shaderSampledImageArrayNonUniformIndexing", &features->shaderSampledImageArrayNonUniformIndexing) &&
-    visitor->Visit("shaderStorageBufferArrayNonUniformIndexing", &features->shaderStorageBufferArrayNonUniformIndexing) &&
-    visitor->Visit("shaderStorageImageArrayNonUniformIndexing", &features->shaderStorageImageArrayNonUniformIndexing) &&
-    visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing", &features->shaderInputAttachmentArrayNonUniformIndexing) &&
-    visitor->Visit("shaderUniformTexelBufferArrayNonUniformIndexing", &features->shaderUniformTexelBufferArrayNonUniformIndexing) &&
-    visitor->Visit("shaderStorageTexelBufferArrayNonUniformIndexing", &features->shaderStorageTexelBufferArrayNonUniformIndexing) &&
-    visitor->Visit("descriptorBindingUniformBufferUpdateAfterBind", &features->descriptorBindingUniformBufferUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingSampledImageUpdateAfterBind", &features->descriptorBindingSampledImageUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingStorageImageUpdateAfterBind", &features->descriptorBindingStorageImageUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingStorageBufferUpdateAfterBind", &features->descriptorBindingStorageBufferUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingUniformTexelBufferUpdateAfterBind", &features->descriptorBindingUniformTexelBufferUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingStorageTexelBufferUpdateAfterBind", &features->descriptorBindingStorageTexelBufferUpdateAfterBind) &&
-    visitor->Visit("descriptorBindingUpdateUnusedWhilePending", &features->descriptorBindingUpdateUnusedWhilePending) &&
-    visitor->Visit("descriptorBindingPartiallyBound", &features->descriptorBindingPartiallyBound) &&
-    visitor->Visit("descriptorBindingVariableDescriptorCount", &features->descriptorBindingVariableDescriptorCount) &&
-    visitor->Visit("runtimeDescriptorArray", &features->runtimeDescriptorArray) &&
-    visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) &&
-    visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) &&
-    visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) &&
-    visitor->Visit("uniformBufferStandardLayout", &features->uniformBufferStandardLayout) &&
-    visitor->Visit("shaderSubgroupExtendedTypes", &features->shaderSubgroupExtendedTypes) &&
-    visitor->Visit("separateDepthStencilLayouts", &features->separateDepthStencilLayouts) &&
-    visitor->Visit("hostQueryReset", &features->hostQueryReset) &&
-    visitor->Visit("timelineSemaphore", &features->timelineSemaphore) &&
-    visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) &&
-    visitor->Visit("bufferDeviceAddressCaptureReplay", &features->bufferDeviceAddressCaptureReplay) &&
-    visitor->Visit("bufferDeviceAddressMultiDevice", &features->bufferDeviceAddressMultiDevice) &&
-    visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) &&
-    visitor->Visit("vulkanMemoryModelDeviceScope", &features->vulkanMemoryModelDeviceScope) &&
-    visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) &&
-    visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) &&
-    visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) &&
-    visitor->Visit("subgroupBroadcastDynamicId", &features->subgroupBroadcastDynamicId);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkJsonCore13* core) {
-  return
-    visitor->Visit("features", &core->features) &&
-    visitor->Visit("properties", &core->properties);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Properties* properties) {
-  return
-    visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) &&
-    visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) &&
-    visitor->Visit("maxComputeWorkgroupSubgroups", &properties->maxComputeWorkgroupSubgroups) &&
-    visitor->Visit("requiredSubgroupSizeStages", &properties->requiredSubgroupSizeStages) &&
-    visitor->Visit("maxInlineUniformBlockSize", &properties->maxInlineUniformBlockSize) &&
-    visitor->Visit("maxPerStageDescriptorInlineUniformBlocks", &properties->maxPerStageDescriptorInlineUniformBlocks) &&
-    visitor->Visit("maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks", &properties->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) &&
-    visitor->Visit("maxDescriptorSetInlineUniformBlocks", &properties->maxDescriptorSetInlineUniformBlocks) &&
-    visitor->Visit("maxDescriptorSetUpdateAfterBindInlineUniformBlocks", &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) &&
-    visitor->Visit("maxInlineUniformTotalSize", &properties->maxInlineUniformTotalSize) &&
-    visitor->Visit("integerDotProduct8BitUnsignedAccelerated", &properties->integerDotProduct8BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProduct8BitSignedAccelerated", &properties->integerDotProduct8BitSignedAccelerated) &&
-    visitor->Visit("integerDotProduct8BitMixedSignednessAccelerated", &properties->integerDotProduct8BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProduct4x8BitPackedUnsignedAccelerated", &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) &&
-    visitor->Visit("integerDotProduct4x8BitPackedSignedAccelerated", &properties->integerDotProduct4x8BitPackedSignedAccelerated) &&
-    visitor->Visit("integerDotProduct4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProduct4x8BitPackedMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProduct16BitUnsignedAccelerated", &properties->integerDotProduct16BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProduct16BitSignedAccelerated", &properties->integerDotProduct16BitSignedAccelerated) &&
-    visitor->Visit("integerDotProduct16BitMixedSignednessAccelerated", &properties->integerDotProduct16BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProduct32BitUnsignedAccelerated", &properties->integerDotProduct32BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProduct32BitSignedAccelerated", &properties->integerDotProduct32BitSignedAccelerated) &&
-    visitor->Visit("integerDotProduct32BitMixedSignednessAccelerated", &properties->integerDotProduct32BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProduct64BitUnsignedAccelerated", &properties->integerDotProduct64BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProduct64BitSignedAccelerated", &properties->integerDotProduct64BitSignedAccelerated) &&
-    visitor->Visit("integerDotProduct64BitMixedSignednessAccelerated", &properties->integerDotProduct64BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating8BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating8BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitSignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating16BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating16BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitSignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating32BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating32BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitSignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating64BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating64BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitSignedAccelerated) &&
-    visitor->Visit("integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) &&
-    visitor->Visit("storageTexelBufferOffsetAlignmentBytes", &properties->storageTexelBufferOffsetAlignmentBytes) &&
-    visitor->Visit("storageTexelBufferOffsetSingleTexelAlignment", &properties->storageTexelBufferOffsetSingleTexelAlignment) &&
-    visitor->Visit("uniformTexelBufferOffsetAlignmentBytes", &properties->uniformTexelBufferOffsetAlignmentBytes) &&
-    visitor->Visit("uniformTexelBufferOffsetSingleTexelAlignment", &properties->uniformTexelBufferOffsetSingleTexelAlignment) &&
-    visitor->Visit("maxBufferSize", &properties->maxBufferSize);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Features* features) {
-  return
-    visitor->Visit("robustImageAccess", &features->robustImageAccess) &&
-    visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) &&
-    visitor->Visit("descriptorBindingInlineUniformBlockUpdateAfterBind", &features->descriptorBindingInlineUniformBlockUpdateAfterBind) &&
-    visitor->Visit("pipelineCreationCacheControl", &features->pipelineCreationCacheControl) &&
-    visitor->Visit("privateData", &features->privateData) &&
-    visitor->Visit("shaderDemoteToHelperInvocation", &features->shaderDemoteToHelperInvocation) &&
-    visitor->Visit("shaderTerminateInvocation", &features->shaderTerminateInvocation) &&
-    visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) &&
-    visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) &&
-    visitor->Visit("synchronization2", &features->synchronization2) &&
-    visitor->Visit("textureCompressionASTC_HDR", &features->textureCompressionASTC_HDR) &&
-    visitor->Visit("shaderZeroInitializeWorkgroupMemory", &features->shaderZeroInitializeWorkgroupMemory) &&
-    visitor->Visit("dynamicRendering", &features->dynamicRendering) &&
-    visitor->Visit("shaderIntegerDotProduct", &features->shaderIntegerDotProduct) &&
-    visitor->Visit("maintenance4", &features->maintenance4);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkJsonCore14* core) {
-  return
-    visitor->Visit("features", &core->features) &&
-    visitor->Visit("properties", &core->properties);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Properties* properties) {
-  return
-    visitor->Visit("lineSubPixelPrecisionBits", &properties->lineSubPixelPrecisionBits) &&
-    visitor->Visit("maxVertexAttribDivisor", &properties->maxVertexAttribDivisor) &&
-    visitor->Visit("supportsNonZeroFirstInstance", &properties->supportsNonZeroFirstInstance) &&
-    visitor->Visit("maxPushDescriptors", &properties->maxPushDescriptors) &&
-    visitor->Visit("dynamicRenderingLocalReadDepthStencilAttachments", &properties->dynamicRenderingLocalReadDepthStencilAttachments) &&
-    visitor->Visit("dynamicRenderingLocalReadMultisampledAttachments", &properties->dynamicRenderingLocalReadMultisampledAttachments) &&
-    visitor->Visit("earlyFragmentMultisampleCoverageAfterSampleCounting", &properties->earlyFragmentMultisampleCoverageAfterSampleCounting) &&
-    visitor->Visit("earlyFragmentSampleMaskTestBeforeSampleCounting", &properties->earlyFragmentSampleMaskTestBeforeSampleCounting) &&
-    visitor->Visit("depthStencilSwizzleOneSupport", &properties->depthStencilSwizzleOneSupport) &&
-    visitor->Visit("polygonModePointSize", &properties->polygonModePointSize) &&
-    visitor->Visit("nonStrictSinglePixelWideLinesUseParallelogram", &properties->nonStrictSinglePixelWideLinesUseParallelogram) &&
-    visitor->Visit("nonStrictWideLinesUseParallelogram", &properties->nonStrictWideLinesUseParallelogram) &&
-    visitor->Visit("blockTexelViewCompatibleMultipleLayers", &properties->blockTexelViewCompatibleMultipleLayers) &&
-    visitor->Visit("maxCombinedImageSamplerDescriptorCount", &properties->maxCombinedImageSamplerDescriptorCount) &&
-    visitor->Visit("fragmentShadingRateClampCombinerInputs", &properties->fragmentShadingRateClampCombinerInputs) &&
-    visitor->Visit("defaultRobustnessStorageBuffers", &properties->defaultRobustnessStorageBuffers) &&
-    visitor->Visit("defaultRobustnessUniformBuffers", &properties->defaultRobustnessUniformBuffers) &&
-    visitor->Visit("defaultRobustnessVertexInputs", &properties->defaultRobustnessVertexInputs) &&
-    visitor->Visit("defaultRobustnessImages", &properties->defaultRobustnessImages) &&
-    visitor->Visit("copySrcLayoutCount", &properties->copySrcLayoutCount) &&
-    visitor->VisitArray("pCopySrcLayouts", properties->copySrcLayoutCount, &properties->pCopySrcLayouts) &&
-    visitor->Visit("copyDstLayoutCount", &properties->copyDstLayoutCount) &&
-    visitor->VisitArray("pCopyDstLayouts", properties->copyDstLayoutCount, &properties->pCopyDstLayouts) &&
-    visitor->Visit("optimalTilingLayoutUUID", &properties->optimalTilingLayoutUUID) &&
-    visitor->Visit("identicalMemoryTypeRequirements", &properties->identicalMemoryTypeRequirements);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Features* features) {
-  return
-    visitor->Visit("globalPriorityQuery", &features->globalPriorityQuery) &&
-    visitor->Visit("shaderSubgroupRotate", &features->shaderSubgroupRotate) &&
-    visitor->Visit("shaderSubgroupRotateClustered", &features->shaderSubgroupRotateClustered) &&
-    visitor->Visit("shaderFloatControls2", &features->shaderFloatControls2) &&
-    visitor->Visit("shaderExpectAssume", &features->shaderExpectAssume) &&
-    visitor->Visit("rectangularLines", &features->rectangularLines) &&
-    visitor->Visit("bresenhamLines", &features->bresenhamLines) &&
-    visitor->Visit("smoothLines", &features->smoothLines) &&
-    visitor->Visit("stippledRectangularLines", &features->stippledRectangularLines) &&
-    visitor->Visit("stippledBresenhamLines", &features->stippledBresenhamLines) &&
-    visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines) &&
-    visitor->Visit("vertexAttributeInstanceRateDivisor", &features->vertexAttributeInstanceRateDivisor) &&
-    visitor->Visit("vertexAttributeInstanceRateZeroDivisor", &features->vertexAttributeInstanceRateZeroDivisor) &&
-    visitor->Visit("indexTypeUint8", &features->indexTypeUint8) &&
-    visitor->Visit("dynamicRenderingLocalRead", &features->dynamicRenderingLocalRead) &&
-    visitor->Visit("maintenance5", &features->maintenance5) &&
-    visitor->Visit("maintenance6", &features->maintenance6) &&
-    visitor->Visit("pipelineProtectedAccess", &features->pipelineProtectedAccess) &&
-    visitor->Visit("pipelineRobustness", &features->pipelineRobustness) &&
-    visitor->Visit("hostImageCopy", &features->hostImageCopy);
-}
-// clang-format on
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkJsonExtDriverProperties* properties) {
-  return visitor->Visit("driverPropertiesKHR",
-                        &properties->driver_properties_khr);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkPhysicalDeviceDriverPropertiesKHR* properties) {
-  return visitor->Visit("driverID", &properties->driverID) &&
-         visitor->Visit("driverName", &properties->driverName) &&
-         visitor->Visit("driverInfo", &properties->driverInfo) &&
-         visitor->Visit("conformanceVersion", &properties->conformanceVersion);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkConformanceVersionKHR* version) {
+inline bool Iterate(Visitor* visitor, VkConformanceVersionKHR* version) {
   return visitor->Visit("major", &version->major) &&
          visitor->Visit("minor", &version->minor) &&
          visitor->Visit("subminor", &version->subminor) &&
@@ -978,144 +499,1412 @@
 }
 
 template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkJsonExtVariablePointerFeatures* features) {
-  return visitor->Visit("variablePointerFeaturesKHR",
-                        &features->variable_pointer_features_khr);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkJsonExtShaderFloat16Int8Features* features) {
-  return visitor->Visit("shaderFloat16Int8FeaturesKHR",
-                        &features->shader_float16_int8_features_khr);
-}
-
-template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkMemoryType* type) {
-  return
-    visitor->Visit("propertyFlags", &type->propertyFlags) &&
-    visitor->Visit("heapIndex", &type->heapIndex);
+  return visitor->Visit("propertyFlags", &type->propertyFlags) &&
+         visitor->Visit("heapIndex", &type->heapIndex);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkMemoryHeap* heap) {
-  return
-    visitor->Visit("size", &heap->size) &&
-    visitor->Visit("flags", &heap->flags);
+  return visitor->Visit("size", &heap->size) &&
+         visitor->Visit("flags", &heap->flags);
 }
 
 template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkPhysicalDeviceMemoryProperties* properties) {
+inline bool Iterate(Visitor* visitor, VkJsonCore11* core) {
+  return visitor->Visit("properties", &core->properties) &&
+         visitor->Visit("features", &core->features);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore12* core) {
+  return visitor->Visit("properties", &core->properties) &&
+         visitor->Visit("features", &core->features);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore13* core) {
+  return visitor->Visit("properties", &core->properties) &&
+         visitor->Visit("features", &core->features);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore14* core) {
+  return visitor->Visit("properties", &core->properties) &&
+         visitor->Visit("features", &core->features);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRVariablePointers* structs) {
+  return visitor->Visit("variablePointerFeaturesKHR",
+                        &structs->variable_pointer_features_khr) &&
+         visitor->Visit("variablePointersFeaturesKHR",
+                        &structs->variable_pointers_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRShaderFloat16Int8* structs) {
+  return visitor->Visit("shaderFloat16Int8FeaturesKHR",
+                        &structs->shader_float16_int8_features_khr) &&
+         visitor->Visit("float16Int8FeaturesKHR",
+                        &structs->float16_int8_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtImage2dViewOf3d* structs) {
+  return visitor->Visit("image2DViewOf3DFeaturesEXT",
+                        &structs->image_2d_view_of_3d_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtCustomBorderColor* structs) {
+  return visitor->Visit("customBorderColorFeaturesEXT",
+                        &structs->custom_border_color_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonExtPrimitiveTopologyListRestart* structs) {
+  return visitor->Visit("primitiveTopologyListRestartFeaturesEXT",
+                        &structs->primitive_topology_list_restart_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtProvokingVertex* structs) {
+  return visitor->Visit("provokingVertexFeaturesEXT",
+                        &structs->provoking_vertex_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRIndexTypeUint8* structs) {
+  return visitor->Visit("indexTypeUint8FeaturesKHR",
+                        &structs->index_type_uint8_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtIndexTypeUint8* structs) {
+  return visitor->Visit("indexTypeUint8FeaturesEXT",
+                        &structs->index_type_uint8_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonKHRVertexAttributeDivisor* structs) {
+  return visitor->Visit("vertexAttributeDivisorFeaturesKHR",
+                        &structs->vertex_attribute_divisor_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonExtVertexAttributeDivisor* structs) {
+  return visitor->Visit("vertexAttributeDivisorFeaturesEXT",
+                        &structs->vertex_attribute_divisor_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtTransformFeedback* structs) {
+  return visitor->Visit("transformFeedbackFeaturesEXT",
+                        &structs->transform_feedback_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonKHRShaderSubgroupUniformControlFlow* structs) {
+  return visitor->Visit(
+      "shaderSubgroupUniformControlFlowFeaturesKHR",
+      &structs->shader_subgroup_uniform_control_flow_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonKHRShaderSubgroupExtendedTypes* structs) {
+  return visitor->Visit("shaderSubgroupExtendedTypesFeaturesKHR",
+                        &structs->shader_subgroup_extended_types_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHR8bitStorage* structs) {
+  return visitor->Visit("bit8StorageFeaturesKHR",
+                        &structs->bit8_storage_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonKHRShaderIntegerDotProduct* structs) {
+  return visitor->Visit("shaderIntegerDotProductFeaturesKHR",
+                        &structs->shader_integer_dot_product_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonIMGRelaxedLineRasterization* structs) {
+  return visitor->Visit("relaxedLineRasterizationFeaturesIMG",
+                        &structs->relaxed_line_rasterization_features_img);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRLineRasterization* structs) {
+  return visitor->Visit("lineRasterizationFeaturesKHR",
+                        &structs->line_rasterization_features_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonExtLineRasterization* structs) {
+  return visitor->Visit("lineRasterizationFeaturesEXT",
+                        &structs->line_rasterization_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonExtPrimitivesGeneratedQuery* structs) {
+  return visitor->Visit("primitivesGeneratedQueryFeaturesEXT",
+                        &structs->primitives_generated_query_features_ext);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRShaderFloatControls* structs) {
+  return visitor->Visit("floatControlsPropertiesKHR",
+                        &structs->float_controls_properties_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonKHRDriverProperties* structs) {
+  return visitor->Visit("driverPropertiesKHR", &structs->driver_properties_khr);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceFloatControlsProperties* properties) {
   return
-    visitor->Visit("memoryTypeCount", &properties->memoryTypeCount) &&
-    visitor->VisitArray("memoryTypes", properties->memoryTypeCount, &properties->memoryTypes) &&
-    visitor->Visit("memoryHeapCount", &properties->memoryHeapCount) &&
-    visitor->VisitArray("memoryHeaps", properties->memoryHeapCount, &properties->memoryHeaps);
+
+      visitor->Visit("denormBehaviorIndependence",
+                     &properties->denormBehaviorIndependence) &&
+      visitor->Visit("roundingModeIndependence",
+                     &properties->roundingModeIndependence) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat16",
+                     &properties->shaderSignedZeroInfNanPreserveFloat16) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat32",
+                     &properties->shaderSignedZeroInfNanPreserveFloat32) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat64",
+                     &properties->shaderSignedZeroInfNanPreserveFloat64) &&
+      visitor->Visit("shaderDenormPreserveFloat16",
+                     &properties->shaderDenormPreserveFloat16) &&
+      visitor->Visit("shaderDenormPreserveFloat32",
+                     &properties->shaderDenormPreserveFloat32) &&
+      visitor->Visit("shaderDenormPreserveFloat64",
+                     &properties->shaderDenormPreserveFloat64) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat16",
+                     &properties->shaderDenormFlushToZeroFloat16) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat32",
+                     &properties->shaderDenormFlushToZeroFloat32) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat64",
+                     &properties->shaderDenormFlushToZeroFloat64) &&
+      visitor->Visit("shaderRoundingModeRTEFloat16",
+                     &properties->shaderRoundingModeRTEFloat16) &&
+      visitor->Visit("shaderRoundingModeRTEFloat32",
+                     &properties->shaderRoundingModeRTEFloat32) &&
+      visitor->Visit("shaderRoundingModeRTEFloat64",
+                     &properties->shaderRoundingModeRTEFloat64) &&
+      visitor->Visit("shaderRoundingModeRTZFloat16",
+                     &properties->shaderRoundingModeRTZFloat16) &&
+      visitor->Visit("shaderRoundingModeRTZFloat32",
+                     &properties->shaderRoundingModeRTZFloat32) &&
+      visitor->Visit("shaderRoundingModeRTZFloat64",
+                     &properties->shaderRoundingModeRTZFloat64);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceProperties* properties) {
+  return
+
+      visitor->Visit("apiVersion", &properties->apiVersion) &&
+      visitor->Visit("driverVersion", &properties->driverVersion) &&
+      visitor->Visit("vendorID", &properties->vendorID) &&
+      visitor->Visit("deviceID", &properties->deviceID) &&
+      visitor->Visit("deviceType", &properties->deviceType) &&
+      visitor->Visit("deviceName", &properties->deviceName) &&
+      visitor->Visit("pipelineCacheUUID", &properties->pipelineCacheUUID) &&
+      visitor->Visit("limits", &properties->limits) &&
+      visitor->Visit("sparseProperties", &properties->sparseProperties);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceMemoryProperties* properties) {
+  return
+
+      visitor->Visit("memoryTypeCount", &properties->memoryTypeCount) &&
+      visitor->VisitArray("memoryTypes", properties->memoryTypeCount,
+                          &properties->memoryTypes) &&
+      visitor->Visit("memoryHeapCount", &properties->memoryHeapCount) &&
+      visitor->VisitArray("memoryHeaps", properties->memoryHeapCount,
+                          &properties->memoryHeaps);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceSubgroupProperties* properties) {
-  return visitor->Visit("subgroupSize", &properties->subgroupSize) &&
-         visitor->Visit("supportedStages", &properties->supportedStages) &&
-         visitor->Visit("supportedOperations",
-                        &properties->supportedOperations) &&
-         visitor->Visit("quadOperationsInAllStages",
-                        &properties->quadOperationsInAllStages);
+  return
+
+      visitor->Visit("subgroupSize", &properties->subgroupSize) &&
+      visitor->Visit("supportedStages", &properties->supportedStages) &&
+      visitor->Visit("supportedOperations", &properties->supportedOperations) &&
+      visitor->Visit("quadOperationsInAllStages",
+                     &properties->quadOperationsInAllStages);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDevicePointClippingProperties* properties) {
-  return visitor->Visit("pointClippingBehavior",
-                        &properties->pointClippingBehavior);
+  return
+
+      visitor->Visit("pointClippingBehavior",
+                     &properties->pointClippingBehavior);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceMultiviewProperties* properties) {
-  return visitor->Visit("maxMultiviewViewCount",
-                        &properties->maxMultiviewViewCount) &&
-         visitor->Visit("maxMultiviewInstanceIndex",
-                        &properties->maxMultiviewInstanceIndex);
+  return
+
+      visitor->Visit("maxMultiviewViewCount",
+                     &properties->maxMultiviewViewCount) &&
+      visitor->Visit("maxMultiviewInstanceIndex",
+                     &properties->maxMultiviewInstanceIndex);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceIDProperties* properties) {
-  return visitor->Visit("deviceUUID", &properties->deviceUUID) &&
-         visitor->Visit("driverUUID", &properties->driverUUID) &&
-         visitor->Visit("deviceLUID", &properties->deviceLUID) &&
-         visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) &&
-         visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid);
+  return
+
+      visitor->Visit("deviceUUID", &properties->deviceUUID) &&
+      visitor->Visit("driverUUID", &properties->driverUUID) &&
+      visitor->Visit("deviceLUID", &properties->deviceLUID) &&
+      visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) &&
+      visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceMaintenance3Properties* properties) {
-  return visitor->Visit("maxPerSetDescriptors",
-                        &properties->maxPerSetDescriptors) &&
-         visitor->Visit("maxMemoryAllocationSize",
-                        &properties->maxMemoryAllocationSize);
+  return
+
+      visitor->Visit("maxPerSetDescriptors",
+                     &properties->maxPerSetDescriptors) &&
+      visitor->Visit("maxMemoryAllocationSize",
+                     &properties->maxMemoryAllocationSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceSparseProperties* properties) {
+  return
+
+      visitor->Visit("residencyStandard2DBlockShape",
+                     &properties->residencyStandard2DBlockShape) &&
+      visitor->Visit("residencyStandard2DMultisampleBlockShape",
+                     &properties->residencyStandard2DMultisampleBlockShape) &&
+      visitor->Visit("residencyStandard3DBlockShape",
+                     &properties->residencyStandard3DBlockShape) &&
+      visitor->Visit("residencyAlignedMipSize",
+                     &properties->residencyAlignedMipSize) &&
+      visitor->Visit("residencyNonResidentStrict",
+                     &properties->residencyNonResidentStrict);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkImageFormatProperties* properties) {
+  return
+
+      visitor->Visit("maxExtent", &properties->maxExtent) &&
+      visitor->Visit("maxMipLevels", &properties->maxMipLevels) &&
+      visitor->Visit("maxArrayLayers", &properties->maxArrayLayers) &&
+      visitor->Visit("sampleCounts", &properties->sampleCounts) &&
+      visitor->Visit("maxResourceSize", &properties->maxResourceSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkQueueFamilyProperties* properties) {
+  return
+
+      visitor->Visit("queueFlags", &properties->queueFlags) &&
+      visitor->Visit("queueCount", &properties->queueCount) &&
+      visitor->Visit("timestampValidBits", &properties->timestampValidBits) &&
+      visitor->Visit("minImageTransferGranularity",
+                     &properties->minImageTransferGranularity);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkExtensionProperties* properties) {
+  return
+
+      visitor->Visit("extensionName", &properties->extensionName) &&
+      visitor->Visit("specVersion", &properties->specVersion);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkLayerProperties* properties) {
+  return
+
+      visitor->Visit("layerName", &properties->layerName) &&
+      visitor->Visit("specVersion", &properties->specVersion) &&
+      visitor->Visit("implementationVersion",
+                     &properties->implementationVersion) &&
+      visitor->Visit("description", &properties->description);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkFormatProperties* properties) {
+  return
+
+      visitor->Visit("linearTilingFeatures",
+                     &properties->linearTilingFeatures) &&
+      visitor->Visit("optimalTilingFeatures",
+                     &properties->optimalTilingFeatures) &&
+      visitor->Visit("bufferFeatures", &properties->bufferFeatures);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVariablePointersFeatures* features) {
+  return
+
+      visitor->Visit("variablePointersStorageBuffer",
+                     &features->variablePointersStorageBuffer) &&
+      visitor->Visit("variablePointers", &features->variablePointers);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceShaderFloat16Int8Features* features) {
+  return
+
+      visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
+      visitor->Visit("shaderInt8", &features->shaderInt8);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceImage2DViewOf3DFeaturesEXT* features) {
+  return
+
+      visitor->Visit("image2DViewOf3D", &features->image2DViewOf3D) &&
+      visitor->Visit("sampler2DViewOf3D", &features->sampler2DViewOf3D);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceCustomBorderColorFeaturesEXT* features) {
+  return
+
+      visitor->Visit("customBorderColors", &features->customBorderColors) &&
+      visitor->Visit("customBorderColorWithoutFormat",
+                     &features->customBorderColorWithoutFormat);
+}
+
+template <typename Visitor>
+inline bool Iterate(
+    Visitor* visitor,
+    VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT* features) {
+  return
+
+      visitor->Visit("primitiveTopologyListRestart",
+                     &features->primitiveTopologyListRestart) &&
+      visitor->Visit("primitiveTopologyPatchListRestart",
+                     &features->primitiveTopologyPatchListRestart);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceProvokingVertexFeaturesEXT* features) {
+  return
+
+      visitor->Visit("provokingVertexLast", &features->provokingVertexLast) &&
+      visitor->Visit("transformFeedbackPreservesProvokingVertex",
+                     &features->transformFeedbackPreservesProvokingVertex);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceIndexTypeUint8Features* features) {
+  return
+
+      visitor->Visit("indexTypeUint8", &features->indexTypeUint8);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVertexAttributeDivisorFeatures* features) {
+  return
+
+      visitor->Visit("vertexAttributeInstanceRateDivisor",
+                     &features->vertexAttributeInstanceRateDivisor) &&
+      visitor->Visit("vertexAttributeInstanceRateZeroDivisor",
+                     &features->vertexAttributeInstanceRateZeroDivisor);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceTransformFeedbackFeaturesEXT* features) {
+  return
+
+      visitor->Visit("transformFeedback", &features->transformFeedback) &&
+      visitor->Visit("geometryStreams", &features->geometryStreams);
+}
+
+template <typename Visitor>
+inline bool Iterate(
+    Visitor* visitor,
+    VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR* features) {
+  return
+
+      visitor->Visit("shaderSubgroupUniformControlFlow",
+                     &features->shaderSubgroupUniformControlFlow);
+}
+
+template <typename Visitor>
+inline bool Iterate(
+    Visitor* visitor,
+    VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures* features) {
+  return
+
+      visitor->Visit("shaderSubgroupExtendedTypes",
+                     &features->shaderSubgroupExtendedTypes);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDevice8BitStorageFeatures* features) {
+  return
+
+      visitor->Visit("storageBuffer8BitAccess",
+                     &features->storageBuffer8BitAccess) &&
+      visitor->Visit("uniformAndStorageBuffer8BitAccess",
+                     &features->uniformAndStorageBuffer8BitAccess) &&
+      visitor->Visit("storagePushConstant8", &features->storagePushConstant8);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceShaderIntegerDotProductFeatures* features) {
+  return
+
+      visitor->Visit("shaderIntegerDotProduct",
+                     &features->shaderIntegerDotProduct);
+}
+
+template <typename Visitor>
+inline bool Iterate(
+    Visitor* visitor,
+    VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG* features) {
+  return
+
+      visitor->Visit("relaxedLineRasterization",
+                     &features->relaxedLineRasterization);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceLineRasterizationFeatures* features) {
+  return
+
+      visitor->Visit("rectangularLines", &features->rectangularLines) &&
+      visitor->Visit("bresenhamLines", &features->bresenhamLines) &&
+      visitor->Visit("smoothLines", &features->smoothLines) &&
+      visitor->Visit("stippledRectangularLines",
+                     &features->stippledRectangularLines) &&
+      visitor->Visit("stippledBresenhamLines",
+                     &features->stippledBresenhamLines) &&
+      visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines);
+}
+
+template <typename Visitor>
+inline bool Iterate(
+    Visitor* visitor,
+    VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT* features) {
+  return
+
+      visitor->Visit("primitivesGeneratedQuery",
+                     &features->primitivesGeneratedQuery) &&
+      visitor->Visit(
+          "primitivesGeneratedQueryWithRasterizerDiscard",
+          &features->primitivesGeneratedQueryWithRasterizerDiscard) &&
+      visitor->Visit("primitivesGeneratedQueryWithNonZeroStreams",
+                     &features->primitivesGeneratedQueryWithNonZeroStreams);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDevice16BitStorageFeatures* features) {
-  return visitor->Visit("storageBuffer16BitAccess",
-                        &features->storageBuffer16BitAccess) &&
-         visitor->Visit("uniformAndStorageBuffer16BitAccess",
-                        &features->uniformAndStorageBuffer16BitAccess) &&
-         visitor->Visit("storagePushConstant16",
-                        &features->storagePushConstant16) &&
-         visitor->Visit("storageInputOutput16",
-                        &features->storageInputOutput16);
+  return
+
+      visitor->Visit("storageBuffer16BitAccess",
+                     &features->storageBuffer16BitAccess) &&
+      visitor->Visit("uniformAndStorageBuffer16BitAccess",
+                     &features->uniformAndStorageBuffer16BitAccess) &&
+      visitor->Visit("storagePushConstant16",
+                     &features->storagePushConstant16) &&
+      visitor->Visit("storageInputOutput16", &features->storageInputOutput16);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceMultiviewFeatures* features) {
-  return visitor->Visit("multiview", &features->multiview) &&
-         visitor->Visit("multiviewGeometryShader",
-                        &features->multiviewGeometryShader) &&
-         visitor->Visit("multiviewTessellationShader",
-                        &features->multiviewTessellationShader);
-}
+  return
 
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkPhysicalDeviceVariablePointerFeatures* features) {
-  return visitor->Visit("variablePointersStorageBuffer",
-                        &features->variablePointersStorageBuffer) &&
-         visitor->Visit("variablePointers", &features->variablePointers);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor,
-                    VkPhysicalDeviceShaderFloat16Int8FeaturesKHR* features) {
-  return visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
-         visitor->Visit("shaderInt8", &features->shaderInt8);
+      visitor->Visit("multiview", &features->multiview) &&
+      visitor->Visit("multiviewGeometryShader",
+                     &features->multiviewGeometryShader) &&
+      visitor->Visit("multiviewTessellationShader",
+                     &features->multiviewTessellationShader);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceProtectedMemoryFeatures* features) {
-  return visitor->Visit("protectedMemory", &features->protectedMemory);
+  return
+
+      visitor->Visit("protectedMemory", &features->protectedMemory);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceSamplerYcbcrConversionFeatures* features) {
-  return visitor->Visit("samplerYcbcrConversion",
-                        &features->samplerYcbcrConversion);
+  return
+
+      visitor->Visit("samplerYcbcrConversion",
+                     &features->samplerYcbcrConversion);
 }
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceShaderDrawParameterFeatures* features) {
-  return visitor->Visit("shaderDrawParameters",
-                        &features->shaderDrawParameters);
+  return
+
+      visitor->Visit("shaderDrawParameters", &features->shaderDrawParameters);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) {
+  return
+
+      visitor->Visit("maxImageDimension1D", &limits->maxImageDimension1D) &&
+      visitor->Visit("maxImageDimension2D", &limits->maxImageDimension2D) &&
+      visitor->Visit("maxImageDimension3D", &limits->maxImageDimension3D) &&
+      visitor->Visit("maxImageDimensionCube", &limits->maxImageDimensionCube) &&
+      visitor->Visit("maxImageArrayLayers", &limits->maxImageArrayLayers) &&
+      visitor->Visit("maxTexelBufferElements",
+                     &limits->maxTexelBufferElements) &&
+      visitor->Visit("maxUniformBufferRange", &limits->maxUniformBufferRange) &&
+      visitor->Visit("maxStorageBufferRange", &limits->maxStorageBufferRange) &&
+      visitor->Visit("maxPushConstantsSize", &limits->maxPushConstantsSize) &&
+      visitor->Visit("maxMemoryAllocationCount",
+                     &limits->maxMemoryAllocationCount) &&
+      visitor->Visit("maxSamplerAllocationCount",
+                     &limits->maxSamplerAllocationCount) &&
+      visitor->Visit("bufferImageGranularity",
+                     &limits->bufferImageGranularity) &&
+      visitor->Visit("sparseAddressSpaceSize",
+                     &limits->sparseAddressSpaceSize) &&
+      visitor->Visit("maxBoundDescriptorSets",
+                     &limits->maxBoundDescriptorSets) &&
+      visitor->Visit("maxPerStageDescriptorSamplers",
+                     &limits->maxPerStageDescriptorSamplers) &&
+      visitor->Visit("maxPerStageDescriptorUniformBuffers",
+                     &limits->maxPerStageDescriptorUniformBuffers) &&
+      visitor->Visit("maxPerStageDescriptorStorageBuffers",
+                     &limits->maxPerStageDescriptorStorageBuffers) &&
+      visitor->Visit("maxPerStageDescriptorSampledImages",
+                     &limits->maxPerStageDescriptorSampledImages) &&
+      visitor->Visit("maxPerStageDescriptorStorageImages",
+                     &limits->maxPerStageDescriptorStorageImages) &&
+      visitor->Visit("maxPerStageDescriptorInputAttachments",
+                     &limits->maxPerStageDescriptorInputAttachments) &&
+      visitor->Visit("maxPerStageResources", &limits->maxPerStageResources) &&
+      visitor->Visit("maxDescriptorSetSamplers",
+                     &limits->maxDescriptorSetSamplers) &&
+      visitor->Visit("maxDescriptorSetUniformBuffers",
+                     &limits->maxDescriptorSetUniformBuffers) &&
+      visitor->Visit("maxDescriptorSetUniformBuffersDynamic",
+                     &limits->maxDescriptorSetUniformBuffersDynamic) &&
+      visitor->Visit("maxDescriptorSetStorageBuffers",
+                     &limits->maxDescriptorSetStorageBuffers) &&
+      visitor->Visit("maxDescriptorSetStorageBuffersDynamic",
+                     &limits->maxDescriptorSetStorageBuffersDynamic) &&
+      visitor->Visit("maxDescriptorSetSampledImages",
+                     &limits->maxDescriptorSetSampledImages) &&
+      visitor->Visit("maxDescriptorSetStorageImages",
+                     &limits->maxDescriptorSetStorageImages) &&
+      visitor->Visit("maxDescriptorSetInputAttachments",
+                     &limits->maxDescriptorSetInputAttachments) &&
+      visitor->Visit("maxVertexInputAttributes",
+                     &limits->maxVertexInputAttributes) &&
+      visitor->Visit("maxVertexInputBindings",
+                     &limits->maxVertexInputBindings) &&
+      visitor->Visit("maxVertexInputAttributeOffset",
+                     &limits->maxVertexInputAttributeOffset) &&
+      visitor->Visit("maxVertexInputBindingStride",
+                     &limits->maxVertexInputBindingStride) &&
+      visitor->Visit("maxVertexOutputComponents",
+                     &limits->maxVertexOutputComponents) &&
+      visitor->Visit("maxTessellationGenerationLevel",
+                     &limits->maxTessellationGenerationLevel) &&
+      visitor->Visit("maxTessellationPatchSize",
+                     &limits->maxTessellationPatchSize) &&
+      visitor->Visit("maxTessellationControlPerVertexInputComponents",
+                     &limits->maxTessellationControlPerVertexInputComponents) &&
+      visitor->Visit(
+          "maxTessellationControlPerVertexOutputComponents",
+          &limits->maxTessellationControlPerVertexOutputComponents) &&
+      visitor->Visit("maxTessellationControlPerPatchOutputComponents",
+                     &limits->maxTessellationControlPerPatchOutputComponents) &&
+      visitor->Visit("maxTessellationControlTotalOutputComponents",
+                     &limits->maxTessellationControlTotalOutputComponents) &&
+      visitor->Visit("maxTessellationEvaluationInputComponents",
+                     &limits->maxTessellationEvaluationInputComponents) &&
+      visitor->Visit("maxTessellationEvaluationOutputComponents",
+                     &limits->maxTessellationEvaluationOutputComponents) &&
+      visitor->Visit("maxGeometryShaderInvocations",
+                     &limits->maxGeometryShaderInvocations) &&
+      visitor->Visit("maxGeometryInputComponents",
+                     &limits->maxGeometryInputComponents) &&
+      visitor->Visit("maxGeometryOutputComponents",
+                     &limits->maxGeometryOutputComponents) &&
+      visitor->Visit("maxGeometryOutputVertices",
+                     &limits->maxGeometryOutputVertices) &&
+      visitor->Visit("maxGeometryTotalOutputComponents",
+                     &limits->maxGeometryTotalOutputComponents) &&
+      visitor->Visit("maxFragmentInputComponents",
+                     &limits->maxFragmentInputComponents) &&
+      visitor->Visit("maxFragmentOutputAttachments",
+                     &limits->maxFragmentOutputAttachments) &&
+      visitor->Visit("maxFragmentDualSrcAttachments",
+                     &limits->maxFragmentDualSrcAttachments) &&
+      visitor->Visit("maxFragmentCombinedOutputResources",
+                     &limits->maxFragmentCombinedOutputResources) &&
+      visitor->Visit("maxComputeSharedMemorySize",
+                     &limits->maxComputeSharedMemorySize) &&
+      visitor->Visit("maxComputeWorkGroupCount",
+                     &limits->maxComputeWorkGroupCount) &&
+      visitor->Visit("maxComputeWorkGroupInvocations",
+                     &limits->maxComputeWorkGroupInvocations) &&
+      visitor->Visit("maxComputeWorkGroupSize",
+                     &limits->maxComputeWorkGroupSize) &&
+      visitor->Visit("subPixelPrecisionBits", &limits->subPixelPrecisionBits) &&
+      visitor->Visit("subTexelPrecisionBits", &limits->subTexelPrecisionBits) &&
+      visitor->Visit("mipmapPrecisionBits", &limits->mipmapPrecisionBits) &&
+      visitor->Visit("maxDrawIndexedIndexValue",
+                     &limits->maxDrawIndexedIndexValue) &&
+      visitor->Visit("maxDrawIndirectCount", &limits->maxDrawIndirectCount) &&
+      visitor->Visit("maxSamplerLodBias", &limits->maxSamplerLodBias) &&
+      visitor->Visit("maxSamplerAnisotropy", &limits->maxSamplerAnisotropy) &&
+      visitor->Visit("maxViewports", &limits->maxViewports) &&
+      visitor->Visit("maxViewportDimensions", &limits->maxViewportDimensions) &&
+      visitor->Visit("viewportBoundsRange", &limits->viewportBoundsRange) &&
+      visitor->Visit("viewportSubPixelBits", &limits->viewportSubPixelBits) &&
+      visitor->Visit("minMemoryMapAlignment", &limits->minMemoryMapAlignment) &&
+      visitor->Visit("minTexelBufferOffsetAlignment",
+                     &limits->minTexelBufferOffsetAlignment) &&
+      visitor->Visit("minUniformBufferOffsetAlignment",
+                     &limits->minUniformBufferOffsetAlignment) &&
+      visitor->Visit("minStorageBufferOffsetAlignment",
+                     &limits->minStorageBufferOffsetAlignment) &&
+      visitor->Visit("minTexelOffset", &limits->minTexelOffset) &&
+      visitor->Visit("maxTexelOffset", &limits->maxTexelOffset) &&
+      visitor->Visit("minTexelGatherOffset", &limits->minTexelGatherOffset) &&
+      visitor->Visit("maxTexelGatherOffset", &limits->maxTexelGatherOffset) &&
+      visitor->Visit("minInterpolationOffset",
+                     &limits->minInterpolationOffset) &&
+      visitor->Visit("maxInterpolationOffset",
+                     &limits->maxInterpolationOffset) &&
+      visitor->Visit("subPixelInterpolationOffsetBits",
+                     &limits->subPixelInterpolationOffsetBits) &&
+      visitor->Visit("maxFramebufferWidth", &limits->maxFramebufferWidth) &&
+      visitor->Visit("maxFramebufferHeight", &limits->maxFramebufferHeight) &&
+      visitor->Visit("maxFramebufferLayers", &limits->maxFramebufferLayers) &&
+      visitor->Visit("framebufferColorSampleCounts",
+                     &limits->framebufferColorSampleCounts) &&
+      visitor->Visit("framebufferDepthSampleCounts",
+                     &limits->framebufferDepthSampleCounts) &&
+      visitor->Visit("framebufferStencilSampleCounts",
+                     &limits->framebufferStencilSampleCounts) &&
+      visitor->Visit("framebufferNoAttachmentsSampleCounts",
+                     &limits->framebufferNoAttachmentsSampleCounts) &&
+      visitor->Visit("maxColorAttachments", &limits->maxColorAttachments) &&
+      visitor->Visit("sampledImageColorSampleCounts",
+                     &limits->sampledImageColorSampleCounts) &&
+      visitor->Visit("sampledImageIntegerSampleCounts",
+                     &limits->sampledImageIntegerSampleCounts) &&
+      visitor->Visit("sampledImageDepthSampleCounts",
+                     &limits->sampledImageDepthSampleCounts) &&
+      visitor->Visit("sampledImageStencilSampleCounts",
+                     &limits->sampledImageStencilSampleCounts) &&
+      visitor->Visit("storageImageSampleCounts",
+                     &limits->storageImageSampleCounts) &&
+      visitor->Visit("maxSampleMaskWords", &limits->maxSampleMaskWords) &&
+      visitor->Visit("timestampComputeAndGraphics",
+                     &limits->timestampComputeAndGraphics) &&
+      visitor->Visit("timestampPeriod", &limits->timestampPeriod) &&
+      visitor->Visit("maxClipDistances", &limits->maxClipDistances) &&
+      visitor->Visit("maxCullDistances", &limits->maxCullDistances) &&
+      visitor->Visit("maxCombinedClipAndCullDistances",
+                     &limits->maxCombinedClipAndCullDistances) &&
+      visitor->Visit("discreteQueuePriorities",
+                     &limits->discreteQueuePriorities) &&
+      visitor->Visit("pointSizeRange", &limits->pointSizeRange) &&
+      visitor->Visit("lineWidthRange", &limits->lineWidthRange) &&
+      visitor->Visit("pointSizeGranularity", &limits->pointSizeGranularity) &&
+      visitor->Visit("lineWidthGranularity", &limits->lineWidthGranularity) &&
+      visitor->Visit("strictLines", &limits->strictLines) &&
+      visitor->Visit("standardSampleLocations",
+                     &limits->standardSampleLocations) &&
+      visitor->Visit("optimalBufferCopyOffsetAlignment",
+                     &limits->optimalBufferCopyOffsetAlignment) &&
+      visitor->Visit("optimalBufferCopyRowPitchAlignment",
+                     &limits->optimalBufferCopyRowPitchAlignment) &&
+      visitor->Visit("nonCoherentAtomSize", &limits->nonCoherentAtomSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceFeatures* features) {
+  return
+
+      visitor->Visit("robustBufferAccess", &features->robustBufferAccess) &&
+      visitor->Visit("fullDrawIndexUint32", &features->fullDrawIndexUint32) &&
+      visitor->Visit("imageCubeArray", &features->imageCubeArray) &&
+      visitor->Visit("independentBlend", &features->independentBlend) &&
+      visitor->Visit("geometryShader", &features->geometryShader) &&
+      visitor->Visit("tessellationShader", &features->tessellationShader) &&
+      visitor->Visit("sampleRateShading", &features->sampleRateShading) &&
+      visitor->Visit("dualSrcBlend", &features->dualSrcBlend) &&
+      visitor->Visit("logicOp", &features->logicOp) &&
+      visitor->Visit("multiDrawIndirect", &features->multiDrawIndirect) &&
+      visitor->Visit("drawIndirectFirstInstance",
+                     &features->drawIndirectFirstInstance) &&
+      visitor->Visit("depthClamp", &features->depthClamp) &&
+      visitor->Visit("depthBiasClamp", &features->depthBiasClamp) &&
+      visitor->Visit("fillModeNonSolid", &features->fillModeNonSolid) &&
+      visitor->Visit("depthBounds", &features->depthBounds) &&
+      visitor->Visit("wideLines", &features->wideLines) &&
+      visitor->Visit("largePoints", &features->largePoints) &&
+      visitor->Visit("alphaToOne", &features->alphaToOne) &&
+      visitor->Visit("multiViewport", &features->multiViewport) &&
+      visitor->Visit("samplerAnisotropy", &features->samplerAnisotropy) &&
+      visitor->Visit("textureCompressionETC2",
+                     &features->textureCompressionETC2) &&
+      visitor->Visit("textureCompressionASTC_LDR",
+                     &features->textureCompressionASTC_LDR) &&
+      visitor->Visit("textureCompressionBC", &features->textureCompressionBC) &&
+      visitor->Visit("occlusionQueryPrecise",
+                     &features->occlusionQueryPrecise) &&
+      visitor->Visit("pipelineStatisticsQuery",
+                     &features->pipelineStatisticsQuery) &&
+      visitor->Visit("vertexPipelineStoresAndAtomics",
+                     &features->vertexPipelineStoresAndAtomics) &&
+      visitor->Visit("fragmentStoresAndAtomics",
+                     &features->fragmentStoresAndAtomics) &&
+      visitor->Visit("shaderTessellationAndGeometryPointSize",
+                     &features->shaderTessellationAndGeometryPointSize) &&
+      visitor->Visit("shaderImageGatherExtended",
+                     &features->shaderImageGatherExtended) &&
+      visitor->Visit("shaderStorageImageExtendedFormats",
+                     &features->shaderStorageImageExtendedFormats) &&
+      visitor->Visit("shaderStorageImageMultisample",
+                     &features->shaderStorageImageMultisample) &&
+      visitor->Visit("shaderStorageImageReadWithoutFormat",
+                     &features->shaderStorageImageReadWithoutFormat) &&
+      visitor->Visit("shaderStorageImageWriteWithoutFormat",
+                     &features->shaderStorageImageWriteWithoutFormat) &&
+      visitor->Visit("shaderUniformBufferArrayDynamicIndexing",
+                     &features->shaderUniformBufferArrayDynamicIndexing) &&
+      visitor->Visit("shaderSampledImageArrayDynamicIndexing",
+                     &features->shaderSampledImageArrayDynamicIndexing) &&
+      visitor->Visit("shaderStorageBufferArrayDynamicIndexing",
+                     &features->shaderStorageBufferArrayDynamicIndexing) &&
+      visitor->Visit("shaderStorageImageArrayDynamicIndexing",
+                     &features->shaderStorageImageArrayDynamicIndexing) &&
+      visitor->Visit("shaderClipDistance", &features->shaderClipDistance) &&
+      visitor->Visit("shaderCullDistance", &features->shaderCullDistance) &&
+      visitor->Visit("shaderFloat64", &features->shaderFloat64) &&
+      visitor->Visit("shaderInt64", &features->shaderInt64) &&
+      visitor->Visit("shaderInt16", &features->shaderInt16) &&
+      visitor->Visit("shaderResourceResidency",
+                     &features->shaderResourceResidency) &&
+      visitor->Visit("shaderResourceMinLod", &features->shaderResourceMinLod) &&
+      visitor->Visit("sparseBinding", &features->sparseBinding) &&
+      visitor->Visit("sparseResidencyBuffer",
+                     &features->sparseResidencyBuffer) &&
+      visitor->Visit("sparseResidencyImage2D",
+                     &features->sparseResidencyImage2D) &&
+      visitor->Visit("sparseResidencyImage3D",
+                     &features->sparseResidencyImage3D) &&
+      visitor->Visit("sparseResidency2Samples",
+                     &features->sparseResidency2Samples) &&
+      visitor->Visit("sparseResidency4Samples",
+                     &features->sparseResidency4Samples) &&
+      visitor->Visit("sparseResidency8Samples",
+                     &features->sparseResidency8Samples) &&
+      visitor->Visit("sparseResidency16Samples",
+                     &features->sparseResidency16Samples) &&
+      visitor->Visit("sparseResidencyAliased",
+                     &features->sparseResidencyAliased) &&
+      visitor->Visit("variableMultisampleRate",
+                     &features->variableMultisampleRate) &&
+      visitor->Visit("inheritedQueries", &features->inheritedQueries);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan11Properties* properties) {
+  return
+
+      visitor->Visit("deviceUUID", &properties->deviceUUID) &&
+      visitor->Visit("driverUUID", &properties->driverUUID) &&
+      visitor->Visit("deviceLUID", &properties->deviceLUID) &&
+      visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) &&
+      visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid) &&
+      visitor->Visit("subgroupSize", &properties->subgroupSize) &&
+      visitor->Visit("subgroupSupportedStages",
+                     &properties->subgroupSupportedStages) &&
+      visitor->Visit("subgroupSupportedOperations",
+                     &properties->subgroupSupportedOperations) &&
+      visitor->Visit("subgroupQuadOperationsInAllStages",
+                     &properties->subgroupQuadOperationsInAllStages) &&
+      visitor->Visit("pointClippingBehavior",
+                     &properties->pointClippingBehavior) &&
+      visitor->Visit("maxMultiviewViewCount",
+                     &properties->maxMultiviewViewCount) &&
+      visitor->Visit("maxMultiviewInstanceIndex",
+                     &properties->maxMultiviewInstanceIndex) &&
+      visitor->Visit("protectedNoFault", &properties->protectedNoFault) &&
+      visitor->Visit("maxPerSetDescriptors",
+                     &properties->maxPerSetDescriptors) &&
+      visitor->Visit("maxMemoryAllocationSize",
+                     &properties->maxMemoryAllocationSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan11Features* features) {
+  return
+
+      visitor->Visit("storageBuffer16BitAccess",
+                     &features->storageBuffer16BitAccess) &&
+      visitor->Visit("uniformAndStorageBuffer16BitAccess",
+                     &features->uniformAndStorageBuffer16BitAccess) &&
+      visitor->Visit("storagePushConstant16",
+                     &features->storagePushConstant16) &&
+      visitor->Visit("storageInputOutput16", &features->storageInputOutput16) &&
+      visitor->Visit("multiview", &features->multiview) &&
+      visitor->Visit("multiviewGeometryShader",
+                     &features->multiviewGeometryShader) &&
+      visitor->Visit("multiviewTessellationShader",
+                     &features->multiviewTessellationShader) &&
+      visitor->Visit("variablePointersStorageBuffer",
+                     &features->variablePointersStorageBuffer) &&
+      visitor->Visit("variablePointers", &features->variablePointers) &&
+      visitor->Visit("protectedMemory", &features->protectedMemory) &&
+      visitor->Visit("samplerYcbcrConversion",
+                     &features->samplerYcbcrConversion) &&
+      visitor->Visit("shaderDrawParameters", &features->shaderDrawParameters);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan12Properties* properties) {
+  return
+
+      visitor->Visit("driverID", &properties->driverID) &&
+      visitor->Visit("driverName", &properties->driverName) &&
+      visitor->Visit("driverInfo", &properties->driverInfo) &&
+      visitor->Visit("conformanceVersion", &properties->conformanceVersion) &&
+      visitor->Visit("denormBehaviorIndependence",
+                     &properties->denormBehaviorIndependence) &&
+      visitor->Visit("roundingModeIndependence",
+                     &properties->roundingModeIndependence) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat16",
+                     &properties->shaderSignedZeroInfNanPreserveFloat16) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat32",
+                     &properties->shaderSignedZeroInfNanPreserveFloat32) &&
+      visitor->Visit("shaderSignedZeroInfNanPreserveFloat64",
+                     &properties->shaderSignedZeroInfNanPreserveFloat64) &&
+      visitor->Visit("shaderDenormPreserveFloat16",
+                     &properties->shaderDenormPreserveFloat16) &&
+      visitor->Visit("shaderDenormPreserveFloat32",
+                     &properties->shaderDenormPreserveFloat32) &&
+      visitor->Visit("shaderDenormPreserveFloat64",
+                     &properties->shaderDenormPreserveFloat64) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat16",
+                     &properties->shaderDenormFlushToZeroFloat16) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat32",
+                     &properties->shaderDenormFlushToZeroFloat32) &&
+      visitor->Visit("shaderDenormFlushToZeroFloat64",
+                     &properties->shaderDenormFlushToZeroFloat64) &&
+      visitor->Visit("shaderRoundingModeRTEFloat16",
+                     &properties->shaderRoundingModeRTEFloat16) &&
+      visitor->Visit("shaderRoundingModeRTEFloat32",
+                     &properties->shaderRoundingModeRTEFloat32) &&
+      visitor->Visit("shaderRoundingModeRTEFloat64",
+                     &properties->shaderRoundingModeRTEFloat64) &&
+      visitor->Visit("shaderRoundingModeRTZFloat16",
+                     &properties->shaderRoundingModeRTZFloat16) &&
+      visitor->Visit("shaderRoundingModeRTZFloat32",
+                     &properties->shaderRoundingModeRTZFloat32) &&
+      visitor->Visit("shaderRoundingModeRTZFloat64",
+                     &properties->shaderRoundingModeRTZFloat64) &&
+      visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools",
+                     &properties->maxUpdateAfterBindDescriptorsInAllPools) &&
+      visitor->Visit(
+          "shaderUniformBufferArrayNonUniformIndexingNative",
+          &properties->shaderUniformBufferArrayNonUniformIndexingNative) &&
+      visitor->Visit(
+          "shaderSampledImageArrayNonUniformIndexingNative",
+          &properties->shaderSampledImageArrayNonUniformIndexingNative) &&
+      visitor->Visit(
+          "shaderStorageBufferArrayNonUniformIndexingNative",
+          &properties->shaderStorageBufferArrayNonUniformIndexingNative) &&
+      visitor->Visit(
+          "shaderStorageImageArrayNonUniformIndexingNative",
+          &properties->shaderStorageImageArrayNonUniformIndexingNative) &&
+      visitor->Visit(
+          "shaderInputAttachmentArrayNonUniformIndexingNative",
+          &properties->shaderInputAttachmentArrayNonUniformIndexingNative) &&
+      visitor->Visit("robustBufferAccessUpdateAfterBind",
+                     &properties->robustBufferAccessUpdateAfterBind) &&
+      visitor->Visit("quadDivergentImplicitLod",
+                     &properties->quadDivergentImplicitLod) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindSamplers",
+          &properties->maxPerStageDescriptorUpdateAfterBindSamplers) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindUniformBuffers",
+          &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindStorageBuffers",
+          &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindSampledImages",
+          &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindStorageImages",
+          &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindInputAttachments",
+          &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) &&
+      visitor->Visit("maxPerStageUpdateAfterBindResources",
+                     &properties->maxPerStageUpdateAfterBindResources) &&
+      visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers",
+                     &properties->maxDescriptorSetUpdateAfterBindSamplers) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindUniformBuffers",
+          &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindUniformBuffersDynamic",
+          &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindStorageBuffers",
+          &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindStorageBuffersDynamic",
+          &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindSampledImages",
+          &properties->maxDescriptorSetUpdateAfterBindSampledImages) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindStorageImages",
+          &properties->maxDescriptorSetUpdateAfterBindStorageImages) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindInputAttachments",
+          &properties->maxDescriptorSetUpdateAfterBindInputAttachments) &&
+      visitor->Visit("supportedDepthResolveModes",
+                     &properties->supportedDepthResolveModes) &&
+      visitor->Visit("supportedStencilResolveModes",
+                     &properties->supportedStencilResolveModes) &&
+      visitor->Visit("independentResolveNone",
+                     &properties->independentResolveNone) &&
+      visitor->Visit("independentResolve", &properties->independentResolve) &&
+      visitor->Visit("filterMinmaxSingleComponentFormats",
+                     &properties->filterMinmaxSingleComponentFormats) &&
+      visitor->Visit("filterMinmaxImageComponentMapping",
+                     &properties->filterMinmaxImageComponentMapping) &&
+      visitor->Visit("maxTimelineSemaphoreValueDifference",
+                     &properties->maxTimelineSemaphoreValueDifference) &&
+      visitor->Visit("framebufferIntegerColorSampleCounts",
+                     &properties->framebufferIntegerColorSampleCounts);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan12Features* features) {
+  return
+
+      visitor->Visit("samplerMirrorClampToEdge",
+                     &features->samplerMirrorClampToEdge) &&
+      visitor->Visit("drawIndirectCount", &features->drawIndirectCount) &&
+      visitor->Visit("storageBuffer8BitAccess",
+                     &features->storageBuffer8BitAccess) &&
+      visitor->Visit("uniformAndStorageBuffer8BitAccess",
+                     &features->uniformAndStorageBuffer8BitAccess) &&
+      visitor->Visit("storagePushConstant8", &features->storagePushConstant8) &&
+      visitor->Visit("shaderBufferInt64Atomics",
+                     &features->shaderBufferInt64Atomics) &&
+      visitor->Visit("shaderSharedInt64Atomics",
+                     &features->shaderSharedInt64Atomics) &&
+      visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
+      visitor->Visit("shaderInt8", &features->shaderInt8) &&
+      visitor->Visit("descriptorIndexing", &features->descriptorIndexing) &&
+      visitor->Visit("shaderInputAttachmentArrayDynamicIndexing",
+                     &features->shaderInputAttachmentArrayDynamicIndexing) &&
+      visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing",
+                     &features->shaderUniformTexelBufferArrayDynamicIndexing) &&
+      visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing",
+                     &features->shaderStorageTexelBufferArrayDynamicIndexing) &&
+      visitor->Visit("shaderUniformBufferArrayNonUniformIndexing",
+                     &features->shaderUniformBufferArrayNonUniformIndexing) &&
+      visitor->Visit("shaderSampledImageArrayNonUniformIndexing",
+                     &features->shaderSampledImageArrayNonUniformIndexing) &&
+      visitor->Visit("shaderStorageBufferArrayNonUniformIndexing",
+                     &features->shaderStorageBufferArrayNonUniformIndexing) &&
+      visitor->Visit("shaderStorageImageArrayNonUniformIndexing",
+                     &features->shaderStorageImageArrayNonUniformIndexing) &&
+      visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing",
+                     &features->shaderInputAttachmentArrayNonUniformIndexing) &&
+      visitor->Visit(
+          "shaderUniformTexelBufferArrayNonUniformIndexing",
+          &features->shaderUniformTexelBufferArrayNonUniformIndexing) &&
+      visitor->Visit(
+          "shaderStorageTexelBufferArrayNonUniformIndexing",
+          &features->shaderStorageTexelBufferArrayNonUniformIndexing) &&
+      visitor->Visit(
+          "descriptorBindingUniformBufferUpdateAfterBind",
+          &features->descriptorBindingUniformBufferUpdateAfterBind) &&
+      visitor->Visit("descriptorBindingSampledImageUpdateAfterBind",
+                     &features->descriptorBindingSampledImageUpdateAfterBind) &&
+      visitor->Visit("descriptorBindingStorageImageUpdateAfterBind",
+                     &features->descriptorBindingStorageImageUpdateAfterBind) &&
+      visitor->Visit(
+          "descriptorBindingStorageBufferUpdateAfterBind",
+          &features->descriptorBindingStorageBufferUpdateAfterBind) &&
+      visitor->Visit(
+          "descriptorBindingUniformTexelBufferUpdateAfterBind",
+          &features->descriptorBindingUniformTexelBufferUpdateAfterBind) &&
+      visitor->Visit(
+          "descriptorBindingStorageTexelBufferUpdateAfterBind",
+          &features->descriptorBindingStorageTexelBufferUpdateAfterBind) &&
+      visitor->Visit("descriptorBindingUpdateUnusedWhilePending",
+                     &features->descriptorBindingUpdateUnusedWhilePending) &&
+      visitor->Visit("descriptorBindingPartiallyBound",
+                     &features->descriptorBindingPartiallyBound) &&
+      visitor->Visit("descriptorBindingVariableDescriptorCount",
+                     &features->descriptorBindingVariableDescriptorCount) &&
+      visitor->Visit("runtimeDescriptorArray",
+                     &features->runtimeDescriptorArray) &&
+      visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) &&
+      visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) &&
+      visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) &&
+      visitor->Visit("uniformBufferStandardLayout",
+                     &features->uniformBufferStandardLayout) &&
+      visitor->Visit("shaderSubgroupExtendedTypes",
+                     &features->shaderSubgroupExtendedTypes) &&
+      visitor->Visit("separateDepthStencilLayouts",
+                     &features->separateDepthStencilLayouts) &&
+      visitor->Visit("hostQueryReset", &features->hostQueryReset) &&
+      visitor->Visit("timelineSemaphore", &features->timelineSemaphore) &&
+      visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) &&
+      visitor->Visit("bufferDeviceAddressCaptureReplay",
+                     &features->bufferDeviceAddressCaptureReplay) &&
+      visitor->Visit("bufferDeviceAddressMultiDevice",
+                     &features->bufferDeviceAddressMultiDevice) &&
+      visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) &&
+      visitor->Visit("vulkanMemoryModelDeviceScope",
+                     &features->vulkanMemoryModelDeviceScope) &&
+      visitor->Visit(
+          "vulkanMemoryModelAvailabilityVisibilityChains",
+          &features->vulkanMemoryModelAvailabilityVisibilityChains) &&
+      visitor->Visit("shaderOutputViewportIndex",
+                     &features->shaderOutputViewportIndex) &&
+      visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) &&
+      visitor->Visit("subgroupBroadcastDynamicId",
+                     &features->subgroupBroadcastDynamicId);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan13Properties* properties) {
+  return
+
+      visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) &&
+      visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) &&
+      visitor->Visit("maxComputeWorkgroupSubgroups",
+                     &properties->maxComputeWorkgroupSubgroups) &&
+      visitor->Visit("requiredSubgroupSizeStages",
+                     &properties->requiredSubgroupSizeStages) &&
+      visitor->Visit("maxInlineUniformBlockSize",
+                     &properties->maxInlineUniformBlockSize) &&
+      visitor->Visit("maxPerStageDescriptorInlineUniformBlocks",
+                     &properties->maxPerStageDescriptorInlineUniformBlocks) &&
+      visitor->Visit(
+          "maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks",
+          &properties
+               ->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) &&
+      visitor->Visit("maxDescriptorSetInlineUniformBlocks",
+                     &properties->maxDescriptorSetInlineUniformBlocks) &&
+      visitor->Visit(
+          "maxDescriptorSetUpdateAfterBindInlineUniformBlocks",
+          &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) &&
+      visitor->Visit("maxInlineUniformTotalSize",
+                     &properties->maxInlineUniformTotalSize) &&
+      visitor->Visit("integerDotProduct8BitUnsignedAccelerated",
+                     &properties->integerDotProduct8BitUnsignedAccelerated) &&
+      visitor->Visit("integerDotProduct8BitSignedAccelerated",
+                     &properties->integerDotProduct8BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct8BitMixedSignednessAccelerated",
+          &properties->integerDotProduct8BitMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct4x8BitPackedUnsignedAccelerated",
+          &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct4x8BitPackedSignedAccelerated",
+          &properties->integerDotProduct4x8BitPackedSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct4x8BitPackedMixedSignednessAccelerated",
+          &properties
+               ->integerDotProduct4x8BitPackedMixedSignednessAccelerated) &&
+      visitor->Visit("integerDotProduct16BitUnsignedAccelerated",
+                     &properties->integerDotProduct16BitUnsignedAccelerated) &&
+      visitor->Visit("integerDotProduct16BitSignedAccelerated",
+                     &properties->integerDotProduct16BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct16BitMixedSignednessAccelerated",
+          &properties->integerDotProduct16BitMixedSignednessAccelerated) &&
+      visitor->Visit("integerDotProduct32BitUnsignedAccelerated",
+                     &properties->integerDotProduct32BitUnsignedAccelerated) &&
+      visitor->Visit("integerDotProduct32BitSignedAccelerated",
+                     &properties->integerDotProduct32BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct32BitMixedSignednessAccelerated",
+          &properties->integerDotProduct32BitMixedSignednessAccelerated) &&
+      visitor->Visit("integerDotProduct64BitUnsignedAccelerated",
+                     &properties->integerDotProduct64BitUnsignedAccelerated) &&
+      visitor->Visit("integerDotProduct64BitSignedAccelerated",
+                     &properties->integerDotProduct64BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProduct64BitMixedSignednessAccelerated",
+          &properties->integerDotProduct64BitMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating8BitUnsignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating8BitSignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating8BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerate"
+          "d",
+          &properties
+               ->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerat"
+          "ed",
+          &properties
+               ->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerate"
+          "d",
+          &properties
+               ->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAc"
+          "celerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating16BitUnsignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating16BitSignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating16BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerat"
+          "ed",
+          &properties
+               ->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating32BitUnsignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating32BitSignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating32BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerat"
+          "ed",
+          &properties
+               ->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating64BitUnsignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating64BitSignedAccelerated",
+          &properties
+               ->integerDotProductAccumulatingSaturating64BitSignedAccelerated) &&
+      visitor->Visit(
+          "integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerat"
+          "ed",
+          &properties
+               ->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) &&
+      visitor->Visit("storageTexelBufferOffsetAlignmentBytes",
+                     &properties->storageTexelBufferOffsetAlignmentBytes) &&
+      visitor->Visit(
+          "storageTexelBufferOffsetSingleTexelAlignment",
+          &properties->storageTexelBufferOffsetSingleTexelAlignment) &&
+      visitor->Visit("uniformTexelBufferOffsetAlignmentBytes",
+                     &properties->uniformTexelBufferOffsetAlignmentBytes) &&
+      visitor->Visit(
+          "uniformTexelBufferOffsetSingleTexelAlignment",
+          &properties->uniformTexelBufferOffsetSingleTexelAlignment) &&
+      visitor->Visit("maxBufferSize", &properties->maxBufferSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan13Features* features) {
+  return
+
+      visitor->Visit("robustImageAccess", &features->robustImageAccess) &&
+      visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) &&
+      visitor->Visit(
+          "descriptorBindingInlineUniformBlockUpdateAfterBind",
+          &features->descriptorBindingInlineUniformBlockUpdateAfterBind) &&
+      visitor->Visit("pipelineCreationCacheControl",
+                     &features->pipelineCreationCacheControl) &&
+      visitor->Visit("privateData", &features->privateData) &&
+      visitor->Visit("shaderDemoteToHelperInvocation",
+                     &features->shaderDemoteToHelperInvocation) &&
+      visitor->Visit("shaderTerminateInvocation",
+                     &features->shaderTerminateInvocation) &&
+      visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) &&
+      visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) &&
+      visitor->Visit("synchronization2", &features->synchronization2) &&
+      visitor->Visit("textureCompressionASTC_HDR",
+                     &features->textureCompressionASTC_HDR) &&
+      visitor->Visit("shaderZeroInitializeWorkgroupMemory",
+                     &features->shaderZeroInitializeWorkgroupMemory) &&
+      visitor->Visit("dynamicRendering", &features->dynamicRendering) &&
+      visitor->Visit("shaderIntegerDotProduct",
+                     &features->shaderIntegerDotProduct) &&
+      visitor->Visit("maintenance4", &features->maintenance4);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan14Properties* properties) {
+  return
+
+      visitor->Visit("lineSubPixelPrecisionBits",
+                     &properties->lineSubPixelPrecisionBits) &&
+      visitor->Visit("maxVertexAttribDivisor",
+                     &properties->maxVertexAttribDivisor) &&
+      visitor->Visit("supportsNonZeroFirstInstance",
+                     &properties->supportsNonZeroFirstInstance) &&
+      visitor->Visit("maxPushDescriptors", &properties->maxPushDescriptors) &&
+      visitor->Visit(
+          "dynamicRenderingLocalReadDepthStencilAttachments",
+          &properties->dynamicRenderingLocalReadDepthStencilAttachments) &&
+      visitor->Visit(
+          "dynamicRenderingLocalReadMultisampledAttachments",
+          &properties->dynamicRenderingLocalReadMultisampledAttachments) &&
+      visitor->Visit(
+          "earlyFragmentMultisampleCoverageAfterSampleCounting",
+          &properties->earlyFragmentMultisampleCoverageAfterSampleCounting) &&
+      visitor->Visit(
+          "earlyFragmentSampleMaskTestBeforeSampleCounting",
+          &properties->earlyFragmentSampleMaskTestBeforeSampleCounting) &&
+      visitor->Visit("depthStencilSwizzleOneSupport",
+                     &properties->depthStencilSwizzleOneSupport) &&
+      visitor->Visit("polygonModePointSize",
+                     &properties->polygonModePointSize) &&
+      visitor->Visit(
+          "nonStrictSinglePixelWideLinesUseParallelogram",
+          &properties->nonStrictSinglePixelWideLinesUseParallelogram) &&
+      visitor->Visit("nonStrictWideLinesUseParallelogram",
+                     &properties->nonStrictWideLinesUseParallelogram) &&
+      visitor->Visit("blockTexelViewCompatibleMultipleLayers",
+                     &properties->blockTexelViewCompatibleMultipleLayers) &&
+      visitor->Visit("maxCombinedImageSamplerDescriptorCount",
+                     &properties->maxCombinedImageSamplerDescriptorCount) &&
+      visitor->Visit("fragmentShadingRateClampCombinerInputs",
+                     &properties->fragmentShadingRateClampCombinerInputs) &&
+      visitor->Visit("defaultRobustnessStorageBuffers",
+                     &properties->defaultRobustnessStorageBuffers) &&
+      visitor->Visit("defaultRobustnessUniformBuffers",
+                     &properties->defaultRobustnessUniformBuffers) &&
+      visitor->Visit("defaultRobustnessVertexInputs",
+                     &properties->defaultRobustnessVertexInputs) &&
+      visitor->Visit("defaultRobustnessImages",
+                     &properties->defaultRobustnessImages) &&
+      visitor->Visit("copySrcLayoutCount", &properties->copySrcLayoutCount) &&
+      visitor->VisitArray("pCopySrcLayouts", properties->copySrcLayoutCount,
+                          &properties->pCopySrcLayouts) &&
+      visitor->Visit("copyDstLayoutCount", &properties->copyDstLayoutCount) &&
+      visitor->VisitArray("pCopyDstLayouts", properties->copyDstLayoutCount,
+                          &properties->pCopyDstLayouts) &&
+      visitor->Visit("optimalTilingLayoutUUID",
+                     &properties->optimalTilingLayoutUUID) &&
+      visitor->Visit("identicalMemoryTypeRequirements",
+                     &properties->identicalMemoryTypeRequirements);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceVulkan14Features* features) {
+  return
+
+      visitor->Visit("globalPriorityQuery", &features->globalPriorityQuery) &&
+      visitor->Visit("shaderSubgroupRotate", &features->shaderSubgroupRotate) &&
+      visitor->Visit("shaderSubgroupRotateClustered",
+                     &features->shaderSubgroupRotateClustered) &&
+      visitor->Visit("shaderFloatControls2", &features->shaderFloatControls2) &&
+      visitor->Visit("shaderExpectAssume", &features->shaderExpectAssume) &&
+      visitor->Visit("rectangularLines", &features->rectangularLines) &&
+      visitor->Visit("bresenhamLines", &features->bresenhamLines) &&
+      visitor->Visit("smoothLines", &features->smoothLines) &&
+      visitor->Visit("stippledRectangularLines",
+                     &features->stippledRectangularLines) &&
+      visitor->Visit("stippledBresenhamLines",
+                     &features->stippledBresenhamLines) &&
+      visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines) &&
+      visitor->Visit("vertexAttributeInstanceRateDivisor",
+                     &features->vertexAttributeInstanceRateDivisor) &&
+      visitor->Visit("vertexAttributeInstanceRateZeroDivisor",
+                     &features->vertexAttributeInstanceRateZeroDivisor) &&
+      visitor->Visit("indexTypeUint8", &features->indexTypeUint8) &&
+      visitor->Visit("dynamicRenderingLocalRead",
+                     &features->dynamicRenderingLocalRead) &&
+      visitor->Visit("maintenance5", &features->maintenance5) &&
+      visitor->Visit("maintenance6", &features->maintenance6) &&
+      visitor->Visit("pipelineProtectedAccess",
+                     &features->pipelineProtectedAccess) &&
+      visitor->Visit("pipelineRobustness", &features->pipelineRobustness) &&
+      visitor->Visit("hostImageCopy", &features->hostImageCopy);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceDriverProperties* properties) {
+  return
+
+      visitor->Visit("driverID", &properties->driverID) &&
+      visitor->Visit("driverName", &properties->driverName) &&
+      visitor->Visit("driverInfo", &properties->driverInfo) &&
+      visitor->Visit("conformanceVersion", &properties->conformanceVersion);
 }
 
 template <typename Visitor>
@@ -1140,39 +1929,6 @@
 }
 
 template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkQueueFamilyProperties* properties) {
-  return
-    visitor->Visit("queueFlags", &properties->queueFlags) &&
-    visitor->Visit("queueCount", &properties->queueCount) &&
-    visitor->Visit("timestampValidBits", &properties->timestampValidBits) &&
-    visitor->Visit("minImageTransferGranularity", &properties->minImageTransferGranularity);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkExtensionProperties* properties) {
-  return
-    visitor->Visit("extensionName", &properties->extensionName) &&
-    visitor->Visit("specVersion", &properties->specVersion);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkLayerProperties* properties) {
-  return
-    visitor->Visit("layerName", &properties->layerName) &&
-    visitor->Visit("specVersion", &properties->specVersion) &&
-    visitor->Visit("implementationVersion", &properties->implementationVersion) &&
-    visitor->Visit("description", &properties->description);
-}
-
-template <typename Visitor>
-inline bool Iterate(Visitor* visitor, VkFormatProperties* properties) {
-  return
-    visitor->Visit("linearTilingFeatures", &properties->linearTilingFeatures) &&
-    visitor->Visit("optimalTilingFeatures", &properties->optimalTilingFeatures) &&
-    visitor->Visit("bufferFeatures", &properties->bufferFeatures);
-}
-
-template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkJsonLayer* layer) {
   return visitor->Visit("properties", &layer->properties) &&
          visitor->Visit("extensions", &layer->extensions);
@@ -1197,6 +1953,7 @@
       ret &= visitor->Visit("core13", &device->core13);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_2:
+      ret &= visitor->Visit("core11", &device->core11);
       ret &= visitor->Visit("core12", &device->core12);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
@@ -1209,17 +1966,17 @@
           visitor->Visit("idProperties", &device->id_properties) &&
           visitor->Visit("maintenance3Properties",
                          &device->maintenance3_properties) &&
-          visitor->Visit("16bitStorageFeatures",
-                         &device->bit16_storage_features) &&
           visitor->Visit("multiviewFeatures", &device->multiview_features) &&
-          visitor->Visit("variablePointerFeatures",
-                         &device->variable_pointer_features) &&
+          visitor->Visit("variablePointersFeatures",
+                         &device->variable_pointers_features) &&
           visitor->Visit("protectedMemoryFeatures",
                          &device->protected_memory_features) &&
           visitor->Visit("samplerYcbcrConversionFeatures",
                          &device->sampler_ycbcr_conversion_features) &&
           visitor->Visit("shaderDrawParameterFeatures",
                          &device->shader_draw_parameter_features) &&
+          visitor->Visit("bit16StorageFeatures",
+                         &device->bit16_storage_features) &&
           visitor->Visit("externalFenceProperties",
                          &device->external_fence_properties) &&
           visitor->Visit("externalSemaphoreProperties",
@@ -1233,17 +1990,90 @@
              visitor->Visit("extensions", &device->extensions) &&
              visitor->Visit("layers", &device->layers) &&
              visitor->Visit("formats", &device->formats);
-      if (device->ext_driver_properties.reported) {
-        ret &= visitor->Visit("VK_KHR_driver_properties",
-                            &device->ext_driver_properties);
-      }
-      if (device->ext_variable_pointer_features.reported) {
+
+      if (device->khr_variable_pointers.reported) {
         ret &= visitor->Visit("VK_KHR_variable_pointers",
-                            &device->ext_variable_pointer_features);
+                              &device->khr_variable_pointers);
       }
-      if (device->ext_shader_float16_int8_features.reported) {
+      if (device->khr_shader_float16_int8.reported) {
         ret &= visitor->Visit("VK_KHR_shader_float16_int8",
-                              &device->ext_shader_float16_int8_features);
+                              &device->khr_shader_float16_int8);
+      }
+      if (device->ext_image_2d_view_of_3d.reported) {
+        ret &= visitor->Visit("VK_EXT_image_2d_view_of_3d",
+                              &device->ext_image_2d_view_of_3d);
+      }
+      if (device->ext_custom_border_color.reported) {
+        ret &= visitor->Visit("VK_EXT_custom_border_color",
+                              &device->ext_custom_border_color);
+      }
+      if (device->ext_primitive_topology_list_restart.reported) {
+        ret &= visitor->Visit("VK_EXT_primitive_topology_list_restart",
+                              &device->ext_primitive_topology_list_restart);
+      }
+      if (device->ext_provoking_vertex.reported) {
+        ret &= visitor->Visit("VK_EXT_provoking_vertex",
+                              &device->ext_provoking_vertex);
+      }
+      if (device->khr_index_type_uint8.reported) {
+        ret &= visitor->Visit("VK_KHR_index_type_uint8",
+                              &device->khr_index_type_uint8);
+      }
+      if (device->ext_index_type_uint8.reported) {
+        ret &= visitor->Visit("VK_EXT_index_type_uint8",
+                              &device->ext_index_type_uint8);
+      }
+      if (device->khr_vertex_attribute_divisor.reported) {
+        ret &= visitor->Visit("VK_KHR_vertex_attribute_divisor",
+                              &device->khr_vertex_attribute_divisor);
+      }
+      if (device->ext_vertex_attribute_divisor.reported) {
+        ret &= visitor->Visit("VK_EXT_vertex_attribute_divisor",
+                              &device->ext_vertex_attribute_divisor);
+      }
+      if (device->ext_transform_feedback.reported) {
+        ret &= visitor->Visit("VK_EXT_transform_feedback",
+                              &device->ext_transform_feedback);
+      }
+      if (device->khr_shader_subgroup_uniform_control_flow.reported) {
+        ret &=
+            visitor->Visit("VK_KHR_shader_subgroup_uniform_control_flow",
+                           &device->khr_shader_subgroup_uniform_control_flow);
+      }
+      if (device->khr_shader_subgroup_extended_types.reported) {
+        ret &= visitor->Visit("VK_KHR_shader_subgroup_extended_types",
+                              &device->khr_shader_subgroup_extended_types);
+      }
+      if (device->khr_8bit_storage.reported) {
+        ret &= visitor->Visit("VK_KHR_8bit_storage", &device->khr_8bit_storage);
+      }
+      if (device->khr_shader_integer_dot_product.reported) {
+        ret &= visitor->Visit("VK_KHR_shader_integer_dot_product",
+                              &device->khr_shader_integer_dot_product);
+      }
+      if (device->img_relaxed_line_rasterization.reported) {
+        ret &= visitor->Visit("VK_IMG_relaxed_line_rasterization",
+                              &device->img_relaxed_line_rasterization);
+      }
+      if (device->khr_line_rasterization.reported) {
+        ret &= visitor->Visit("VK_KHR_line_rasterization",
+                              &device->khr_line_rasterization);
+      }
+      if (device->ext_line_rasterization.reported) {
+        ret &= visitor->Visit("VK_EXT_line_rasterization",
+                              &device->ext_line_rasterization);
+      }
+      if (device->ext_primitives_generated_query.reported) {
+        ret &= visitor->Visit("VK_EXT_primitives_generated_query",
+                              &device->ext_primitives_generated_query);
+      }
+      if (device->khr_shader_float_controls.reported) {
+        ret &= visitor->Visit("VK_KHR_shader_float_controls",
+                              &device->khr_shader_float_controls);
+      }
+      if (device->khr_driver_properties.reported) {
+        ret &= visitor->Visit("VK_KHR_driver_properties",
+                              &device->khr_driver_properties);
       }
   }
   return ret;
@@ -1301,7 +2131,9 @@
   return Json::Value(string);
 }
 
-template <typename T, typename = EnableForEnum<T>, typename = void,
+template <typename T,
+          typename = EnableForEnum<T>,
+          typename = void,
           typename = void>
 inline Json::Value ToJsonValue(const T& value) {
   return Json::Value(static_cast<double>(value));
@@ -1310,7 +2142,8 @@
 template <typename T>
 inline Json::Value ArrayToJsonValue(uint32_t count, const T* values) {
   Json::Value array(Json::arrayValue);
-  for (unsigned int i = 0; i < count; ++i) array.append(ToJsonValue(values[i]));
+  for (unsigned int i = 0; i < count; ++i)
+    array.append(ToJsonValue(values[i]));
   return array;
 }
 
@@ -1342,7 +2175,8 @@
 template <typename F, typename S>
 inline Json::Value ToJsonValue(const std::map<F, S>& value) {
   Json::Value array(Json::arrayValue);
-  for (auto& kv : value) array.append(ToJsonValue(kv));
+  for (auto& kv : value)
+    array.append(ToJsonValue(kv));
   return array;
 }
 
@@ -1352,7 +2186,8 @@
 
   ~JsonWriterVisitor() {}
 
-  template <typename T> bool Visit(const char* key, const T* value) {
+  template <typename T>
+  bool Visit(const char* key, const T* value) {
     object_[key] = ToJsonValue(*value);
     return true;
   }
@@ -1365,7 +2200,7 @@
   }
 
   template <typename T>
-  bool VisitArray(const char* key, uint32_t count, const T *value) {
+  bool VisitArray(const char* key, uint32_t count, const T* value) {
     object_[key] = ArrayToJsonValue(count, *value);
     return true;
   }
@@ -1392,7 +2227,8 @@
 bool AsValue(Json::Value* json_value, T* t);
 
 inline bool AsValue(Json::Value* json_value, int32_t* value) {
-  if (json_value->type() != Json::realValue) return false;
+  if (json_value->type() != Json::realValue)
+    return false;
   double d = json_value->asDouble();
   if (!IsIntegral(d) ||
       d < static_cast<double>(std::numeric_limits<int32_t>::min()) ||
@@ -1403,14 +2239,16 @@
 }
 
 inline bool AsValue(Json::Value* json_value, uint64_t* value) {
-  if (json_value->type() != Json::stringValue) return false;
+  if (json_value->type() != Json::stringValue)
+    return false;
   int result =
       std::sscanf(json_value->asString().c_str(), "0x%016" PRIx64, value);
   return result == 1;
 }
 
 inline bool AsValue(Json::Value* json_value, uint32_t* value) {
-  if (json_value->type() != Json::realValue) return false;
+  if (json_value->type() != Json::realValue)
+    return false;
   double d = json_value->asDouble();
   if (!IsIntegral(d) || d < 0.0 ||
       d > static_cast<double>(std::numeric_limits<uint32_t>::max()))
@@ -1429,7 +2267,8 @@
 }
 
 inline bool AsValue(Json::Value* json_value, float* value) {
-  if (json_value->type() != Json::realValue) return false;
+  if (json_value->type() != Json::realValue)
+    return false;
   *value = static_cast<float>(json_value->asDouble());
   return true;
 }
@@ -1438,7 +2277,8 @@
   uint32_t value = 0;
   if (!AsValue(json_value, &value))
     return false;
-  if (!EnumTraits<VkImageLayout>::exist(value)) return false;
+  if (!EnumTraits<VkImageLayout>::exist(value))
+    return false;
   *t = static_cast<VkImageLayout>(value);
   return true;
 }
@@ -1448,7 +2288,8 @@
   if (json_value->type() != Json::arrayValue || json_value->size() != count)
     return false;
   for (uint32_t i = 0; i < count; ++i) {
-    if (!AsValue(&(*json_value)[i], values + i)) return false;
+    if (!AsValue(&(*json_value)[i], values + i))
+      return false;
   }
   return true;
 }
@@ -1460,12 +2301,13 @@
 
 template <size_t N>
 inline bool AsValue(Json::Value* json_value, char (*value)[N]) {
-  if (json_value->type() != Json::stringValue) return false;
+  if (json_value->type() != Json::stringValue)
+    return false;
   size_t len = json_value->asString().length();
   if (len >= N)
     return false;
   memcpy(*value, json_value->asString().c_str(), len);
-  memset(*value + len, 0, N-len);
+  memset(*value + len, 0, N - len);
   return true;
 }
 
@@ -1473,15 +2315,17 @@
 inline bool AsValue(Json::Value* json_value, T* t) {
   uint32_t value = 0;
   if (!AsValue(json_value, &value))
-      return false;
-  if (!EnumTraits<T>::exist(value)) return false;
+    return false;
+  if (!EnumTraits<T>::exist(value))
+    return false;
   *t = static_cast<T>(value);
   return true;
 }
 
 template <typename T>
 inline bool AsValue(Json::Value* json_value, std::vector<T>* value) {
-  if (json_value->type() != Json::arrayValue) return false;
+  if (json_value->type() != Json::arrayValue)
+    return false;
   int size = json_value->size();
   value->resize(size);
   return AsArray(json_value, size, value->data());
@@ -1497,11 +2341,13 @@
 
 template <typename F, typename S>
 inline bool AsValue(Json::Value* json_value, std::map<F, S>* value) {
-  if (json_value->type() != Json::arrayValue) return false;
+  if (json_value->type() != Json::arrayValue)
+    return false;
   int size = json_value->size();
   for (int i = 0; i < size; ++i) {
     std::pair<F, S> elem;
-    if (!AsValue(&(*json_value)[i], &elem)) return false;
+    if (!AsValue(&(*json_value)[i], &elem))
+      return false;
     if (!value->insert(elem).second)
       return false;
   }
@@ -1509,7 +2355,9 @@
 }
 
 template <typename T>
-bool ReadValue(Json::Value* object, const char* key, T* value,
+bool ReadValue(Json::Value* object,
+               const char* key,
+               T* value,
                std::string* errors) {
   Json::Value json_value = (*object)[key];
   if (!json_value) {
@@ -1517,7 +2365,8 @@
       *errors = std::string(key) + " missing.";
     return false;
   }
-  if (AsValue(&json_value, value)) return true;
+  if (AsValue(&json_value, value))
+    return true;
   if (errors)
     *errors = std::string("Wrong type for ") + std::string(key) + ".";
   return false;
@@ -1533,7 +2382,8 @@
   JsonReaderVisitor(Json::Value* object, std::string* errors)
       : object_(object), errors_(errors) {}
 
-  template <typename T> bool Visit(const char* key, T* value) const {
+  template <typename T>
+  bool Visit(const char* key, T* value) const {
     return ReadValue(object_, key, value, errors_);
   }
 
@@ -1547,27 +2397,28 @@
         *errors_ = std::string(key) + " missing.";
       return false;
     }
-    if (AsArray(&json_value, count, *value)) return true;
+    if (AsArray(&json_value, count, *value))
+      return true;
     if (errors_)
       *errors_ = std::string("Wrong type for ") + std::string(key) + ".";
     return false;
   }
 
   template <typename T>
-  bool VisitArray(const char* key, uint32_t count, T *value) {
+  bool VisitArray(const char* key, uint32_t count, T* value) {
     Json::Value json_value = (*object_)[key];
     if (!json_value) {
       if (errors_)
         *errors_ = std::string(key) + " missing.";
       return false;
     }
-    if (AsArray(&json_value, count, *value)) return true;
+    if (AsArray(&json_value, count, *value))
+      return true;
     if (errors_)
       *errors_ = std::string("Wrong type for ") + std::string(key) + ".";
     return false;
   }
 
-
  private:
   Json::Value* object_;
   std::string* errors_;
@@ -1575,21 +2426,21 @@
 
 template <typename T, typename /*= EnableForStruct<T>*/>
 bool AsValue(Json::Value* json_value, T* t) {
-  if (json_value->type() != Json::objectValue) return false;
+  if (json_value->type() != Json::objectValue)
+    return false;
   JsonReaderVisitor visitor(json_value, nullptr);
   return VisitForRead(&visitor, t);
 }
 
-
-template <typename T> std::string VkTypeToJson(const T& t) {
+template <typename T>
+std::string VkTypeToJson(const T& t) {
   JsonWriterVisitor visitor;
   VisitForWrite(&visitor, t);
   return visitor.get_object().toStyledString();
 }
 
-template <typename T> bool VkTypeFromJson(const std::string& json,
-                                          T* t,
-                                          std::string* errors) {
+template <typename T>
+bool VkTypeFromJson(const std::string& json, T* t, std::string* errors) {
   *t = T();
   Json::Value object(Json::objectValue);
   Json::CharReaderBuilder builder;
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 5818c73..d74644c 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -21,8 +21,8 @@
 #ifndef VKJSON_H_
 #define VKJSON_H_
 
-#include <vulkan/vulkan.h>
 #include <string.h>
+#include <vulkan/vulkan.h>
 
 #include <map>
 #include <string>
@@ -38,8 +38,222 @@
   std::vector<VkExtensionProperties> extensions;
 };
 
-struct VkJsonExtDriverProperties {
-  VkJsonExtDriverProperties() {
+struct VkJsonKHRVariablePointers {
+  VkJsonKHRVariablePointers() {
+    reported = false;
+    memset(&variable_pointer_features_khr, 0,
+           sizeof(VkPhysicalDeviceVariablePointerFeaturesKHR));
+    memset(&variable_pointers_features_khr, 0,
+           sizeof(VkPhysicalDeviceVariablePointersFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr;
+  VkPhysicalDeviceVariablePointersFeaturesKHR variable_pointers_features_khr;
+};
+
+struct VkJsonKHRShaderFloat16Int8 {
+  VkJsonKHRShaderFloat16Int8() {
+    reported = false;
+    memset(&shader_float16_int8_features_khr, 0,
+           sizeof(VkPhysicalDeviceShaderFloat16Int8FeaturesKHR));
+    memset(&float16_int8_features_khr, 0,
+           sizeof(VkPhysicalDeviceFloat16Int8FeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr;
+  VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features_khr;
+};
+
+struct VkJsonExtImage2dViewOf3d {
+  VkJsonExtImage2dViewOf3d() {
+    reported = false;
+    memset(&image_2d_view_of_3d_features_ext, 0,
+           sizeof(VkPhysicalDeviceImage2DViewOf3DFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceImage2DViewOf3DFeaturesEXT image_2d_view_of_3d_features_ext;
+};
+
+struct VkJsonExtCustomBorderColor {
+  VkJsonExtCustomBorderColor() {
+    reported = false;
+    memset(&custom_border_color_features_ext, 0,
+           sizeof(VkPhysicalDeviceCustomBorderColorFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border_color_features_ext;
+};
+
+struct VkJsonExtPrimitiveTopologyListRestart {
+  VkJsonExtPrimitiveTopologyListRestart() {
+    reported = false;
+    memset(&primitive_topology_list_restart_features_ext, 0,
+           sizeof(VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT
+      primitive_topology_list_restart_features_ext;
+};
+
+struct VkJsonExtProvokingVertex {
+  VkJsonExtProvokingVertex() {
+    reported = false;
+    memset(&provoking_vertex_features_ext, 0,
+           sizeof(VkPhysicalDeviceProvokingVertexFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_features_ext;
+};
+
+struct VkJsonKHRIndexTypeUint8 {
+  VkJsonKHRIndexTypeUint8() {
+    reported = false;
+    memset(&index_type_uint8_features_khr, 0,
+           sizeof(VkPhysicalDeviceIndexTypeUint8FeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceIndexTypeUint8FeaturesKHR index_type_uint8_features_khr;
+};
+
+struct VkJsonExtIndexTypeUint8 {
+  VkJsonExtIndexTypeUint8() {
+    reported = false;
+    memset(&index_type_uint8_features_ext, 0,
+           sizeof(VkPhysicalDeviceIndexTypeUint8FeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8_features_ext;
+};
+
+struct VkJsonKHRVertexAttributeDivisor {
+  VkJsonKHRVertexAttributeDivisor() {
+    reported = false;
+    memset(&vertex_attribute_divisor_features_khr, 0,
+           sizeof(VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR
+      vertex_attribute_divisor_features_khr;
+};
+
+struct VkJsonExtVertexAttributeDivisor {
+  VkJsonExtVertexAttributeDivisor() {
+    reported = false;
+    memset(&vertex_attribute_divisor_features_ext, 0,
+           sizeof(VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT
+      vertex_attribute_divisor_features_ext;
+};
+
+struct VkJsonExtTransformFeedback {
+  VkJsonExtTransformFeedback() {
+    reported = false;
+    memset(&transform_feedback_features_ext, 0,
+           sizeof(VkPhysicalDeviceTransformFeedbackFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback_features_ext;
+};
+
+struct VkJsonKHRShaderSubgroupUniformControlFlow {
+  VkJsonKHRShaderSubgroupUniformControlFlow() {
+    reported = false;
+    memset(&shader_subgroup_uniform_control_flow_features_khr, 0,
+           sizeof(VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR
+      shader_subgroup_uniform_control_flow_features_khr;
+};
+
+struct VkJsonKHRShaderSubgroupExtendedTypes {
+  VkJsonKHRShaderSubgroupExtendedTypes() {
+    reported = false;
+    memset(&shader_subgroup_extended_types_features_khr, 0,
+           sizeof(VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR
+      shader_subgroup_extended_types_features_khr;
+};
+
+struct VkJsonKHR8bitStorage {
+  VkJsonKHR8bitStorage() {
+    reported = false;
+    memset(&bit8_storage_features_khr, 0,
+           sizeof(VkPhysicalDevice8BitStorageFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage_features_khr;
+};
+
+struct VkJsonKHRShaderIntegerDotProduct {
+  VkJsonKHRShaderIntegerDotProduct() {
+    reported = false;
+    memset(&shader_integer_dot_product_features_khr, 0,
+           sizeof(VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR
+      shader_integer_dot_product_features_khr;
+};
+
+struct VkJsonIMGRelaxedLineRasterization {
+  VkJsonIMGRelaxedLineRasterization() {
+    reported = false;
+    memset(&relaxed_line_rasterization_features_img, 0,
+           sizeof(VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG));
+  }
+  bool reported;
+  VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG
+      relaxed_line_rasterization_features_img;
+};
+
+struct VkJsonKHRLineRasterization {
+  VkJsonKHRLineRasterization() {
+    reported = false;
+    memset(&line_rasterization_features_khr, 0,
+           sizeof(VkPhysicalDeviceLineRasterizationFeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceLineRasterizationFeaturesKHR line_rasterization_features_khr;
+};
+
+struct VkJsonExtLineRasterization {
+  VkJsonExtLineRasterization() {
+    reported = false;
+    memset(&line_rasterization_features_ext, 0,
+           sizeof(VkPhysicalDeviceLineRasterizationFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_features_ext;
+};
+
+struct VkJsonExtPrimitivesGeneratedQuery {
+  VkJsonExtPrimitivesGeneratedQuery() {
+    reported = false;
+    memset(&primitives_generated_query_features_ext, 0,
+           sizeof(VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT));
+  }
+  bool reported;
+  VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT
+      primitives_generated_query_features_ext;
+};
+
+struct VkJsonKHRShaderFloatControls {
+  VkJsonKHRShaderFloatControls() {
+    reported = false;
+    memset(&float_controls_properties_khr, 0,
+           sizeof(VkPhysicalDeviceFloatControlsPropertiesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceFloatControlsPropertiesKHR float_controls_properties_khr;
+};
+
+struct VkJsonKHRDriverProperties {
+  VkJsonKHRDriverProperties() {
     reported = false;
     memset(&driver_properties_khr, 0,
            sizeof(VkPhysicalDeviceDriverPropertiesKHR));
@@ -48,24 +262,9 @@
   VkPhysicalDeviceDriverPropertiesKHR driver_properties_khr;
 };
 
-struct VkJsonExtVariablePointerFeatures {
-  VkJsonExtVariablePointerFeatures() {
-    reported = false;
-    memset(&variable_pointer_features_khr, 0,
-           sizeof(VkPhysicalDeviceVariablePointerFeaturesKHR));
-  }
-  bool reported;
-  VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr;
-};
-
-struct VkJsonExtShaderFloat16Int8Features {
-  VkJsonExtShaderFloat16Int8Features() {
-    reported = false;
-    memset(&shader_float16_int8_features_khr, 0,
-           sizeof(VkPhysicalDeviceShaderFloat16Int8FeaturesKHR));
-  }
-  bool reported;
-  VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr;
+struct VkJsonCore11 {
+  VkPhysicalDeviceVulkan11Properties properties;
+  VkPhysicalDeviceVulkan11Features features;
 };
 
 struct VkJsonCore12 {
@@ -99,28 +298,48 @@
     memset(&bit16_storage_features, 0,
            sizeof(VkPhysicalDevice16BitStorageFeatures));
     memset(&multiview_features, 0, sizeof(VkPhysicalDeviceMultiviewFeatures));
-    memset(&variable_pointer_features, 0,
-           sizeof(VkPhysicalDeviceVariablePointerFeatures));
+    memset(&variable_pointers_features, 0,
+           sizeof(VkPhysicalDeviceVariablePointersFeatures));
     memset(&protected_memory_features, 0,
            sizeof(VkPhysicalDeviceProtectedMemoryFeatures));
     memset(&sampler_ycbcr_conversion_features, 0,
            sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
     memset(&shader_draw_parameter_features, 0,
            sizeof(VkPhysicalDeviceShaderDrawParameterFeatures));
+    memset(&core11, 0, sizeof(VkJsonCore11));
     memset(&core12, 0, sizeof(VkJsonCore12));
     memset(&core13, 0, sizeof(VkJsonCore13));
     memset(&core14, 0, sizeof(VkJsonCore14));
   }
+  VkJsonKHRVariablePointers khr_variable_pointers;
+  VkJsonKHRShaderFloat16Int8 khr_shader_float16_int8;
+  VkJsonExtImage2dViewOf3d ext_image_2d_view_of_3d;
+  VkJsonExtCustomBorderColor ext_custom_border_color;
+  VkJsonExtPrimitiveTopologyListRestart ext_primitive_topology_list_restart;
+  VkJsonExtProvokingVertex ext_provoking_vertex;
+  VkJsonKHRIndexTypeUint8 khr_index_type_uint8;
+  VkJsonExtIndexTypeUint8 ext_index_type_uint8;
+  VkJsonKHRVertexAttributeDivisor khr_vertex_attribute_divisor;
+  VkJsonExtVertexAttributeDivisor ext_vertex_attribute_divisor;
+  VkJsonExtTransformFeedback ext_transform_feedback;
+  VkJsonKHRShaderSubgroupUniformControlFlow
+      khr_shader_subgroup_uniform_control_flow;
+  VkJsonKHRShaderSubgroupExtendedTypes khr_shader_subgroup_extended_types;
+  VkJsonKHR8bitStorage khr_8bit_storage;
+  VkJsonKHRShaderIntegerDotProduct khr_shader_integer_dot_product;
+  VkJsonIMGRelaxedLineRasterization img_relaxed_line_rasterization;
+  VkJsonKHRLineRasterization khr_line_rasterization;
+  VkJsonExtLineRasterization ext_line_rasterization;
+  VkJsonExtPrimitivesGeneratedQuery ext_primitives_generated_query;
+  VkJsonKHRShaderFloatControls khr_shader_float_controls;
+  VkJsonKHRDriverProperties khr_driver_properties;
+  VkJsonCore11 core11;
+  VkJsonCore12 core12;
+  VkJsonCore13 core13;
+  VkJsonCore14 core14;
   VkPhysicalDeviceProperties properties;
   VkPhysicalDeviceFeatures features;
-  VkJsonExtDriverProperties ext_driver_properties;
-  VkJsonExtVariablePointerFeatures ext_variable_pointer_features;
-  VkJsonExtShaderFloat16Int8Features ext_shader_float16_int8_features;
   VkPhysicalDeviceMemoryProperties memory;
-  std::vector<VkQueueFamilyProperties> queues;
-  std::vector<VkExtensionProperties> extensions;
-  std::vector<VkLayerProperties> layers;
-  std::map<VkFormat, VkFormatProperties> formats;
   VkPhysicalDeviceSubgroupProperties subgroup_properties;
   VkPhysicalDevicePointClippingProperties point_clipping_properties;
   VkPhysicalDeviceMultiviewProperties multiview_properties;
@@ -128,18 +347,19 @@
   VkPhysicalDeviceMaintenance3Properties maintenance3_properties;
   VkPhysicalDevice16BitStorageFeatures bit16_storage_features;
   VkPhysicalDeviceMultiviewFeatures multiview_features;
-  VkPhysicalDeviceVariablePointerFeatures variable_pointer_features;
+  VkPhysicalDeviceVariablePointersFeatures variable_pointers_features;
   VkPhysicalDeviceProtectedMemoryFeatures protected_memory_features;
   VkPhysicalDeviceSamplerYcbcrConversionFeatures
       sampler_ycbcr_conversion_features;
   VkPhysicalDeviceShaderDrawParameterFeatures shader_draw_parameter_features;
+  std::vector<VkQueueFamilyProperties> queues;
+  std::vector<VkExtensionProperties> extensions;
+  std::vector<VkLayerProperties> layers;
+  std::map<VkFormat, VkFormatProperties> formats;
   std::map<VkExternalFenceHandleTypeFlagBits, VkExternalFenceProperties>
       external_fence_properties;
   std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties>
       external_semaphore_properties;
-  VkJsonCore12 core12;
-  VkJsonCore13 core13;
-  VkJsonCore14 core14;
 };
 
 struct VkJsonDeviceGroup {
@@ -193,4 +413,4 @@
   return VkJsonDeviceFromJson(json, properties, errors);
 }
 
-#endif  // VKJSON_H_
+#endif  // VKJSON_H_
\ No newline at end of file
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 32bc50b..22b3204 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -79,13 +79,25 @@
       nullptr,
       {},
   };
-  if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
-    device.ext_driver_properties.reported = true;
-    device.ext_driver_properties.driver_properties_khr.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
-    device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext;
-    properties.pNext = &device.ext_driver_properties.driver_properties_khr;
+
+  if (HasExtension("VK_KHR_shader_float_controls", device.extensions)) {
+    device.khr_shader_float_controls.reported = true;
+    device.khr_shader_float_controls.float_controls_properties_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
+    device.khr_shader_float_controls.float_controls_properties_khr.pNext =
+        properties.pNext;
+    properties.pNext =
+        &device.khr_shader_float_controls.float_controls_properties_khr;
   }
+
+  if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
+    device.khr_driver_properties.reported = true;
+    device.khr_driver_properties.driver_properties_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
+    device.khr_driver_properties.driver_properties_khr.pNext = properties.pNext;
+    properties.pNext = &device.khr_driver_properties.driver_properties_khr;
+  }
+
   vkGetPhysicalDeviceProperties2(physical_device, &properties);
   device.properties = properties.properties;
 
@@ -94,25 +106,215 @@
       nullptr,
       {},
   };
+
   if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
-    device.ext_variable_pointer_features.reported = true;
-    device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
-    device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
+    device.khr_variable_pointers.reported = true;
+    device.khr_variable_pointers.variable_pointer_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+    device.khr_variable_pointers.variable_pointer_features_khr.pNext =
         features.pNext;
     features.pNext =
-        &device.ext_variable_pointer_features.variable_pointer_features_khr;
+        &device.khr_variable_pointers.variable_pointer_features_khr;
+    device.khr_variable_pointers.variable_pointers_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+    device.khr_variable_pointers.variable_pointers_features_khr.pNext =
+        features.pNext;
+    features.pNext =
+        &device.khr_variable_pointers.variable_pointers_features_khr;
   }
+
   if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
-    device.ext_shader_float16_int8_features.reported = true;
-    device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
-        .sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
-    device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
-        .pNext = features.pNext;
-    features.pNext = &device.ext_shader_float16_int8_features
-                          .shader_float16_int8_features_khr;
+    device.khr_shader_float16_int8.reported = true;
+    device.khr_shader_float16_int8.shader_float16_int8_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
+    device.khr_shader_float16_int8.shader_float16_int8_features_khr.pNext =
+        features.pNext;
+    features.pNext =
+        &device.khr_shader_float16_int8.shader_float16_int8_features_khr;
+    device.khr_shader_float16_int8.float16_int8_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
+    device.khr_shader_float16_int8.float16_int8_features_khr.pNext =
+        features.pNext;
+    features.pNext = &device.khr_shader_float16_int8.float16_int8_features_khr;
   }
+
+  if (HasExtension("VK_EXT_image_2d_view_of_3d", device.extensions)) {
+    device.ext_image_2d_view_of_3d.reported = true;
+    device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT;
+    device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext.pNext =
+        features.pNext;
+    features.pNext =
+        &device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext;
+  }
+
+  if (HasExtension("VK_EXT_custom_border_color", device.extensions)) {
+    device.ext_custom_border_color.reported = true;
+    device.ext_custom_border_color.custom_border_color_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
+    device.ext_custom_border_color.custom_border_color_features_ext.pNext =
+        features.pNext;
+    features.pNext =
+        &device.ext_custom_border_color.custom_border_color_features_ext;
+  }
+
+  if (HasExtension("VK_EXT_primitive_topology_list_restart",
+                   device.extensions)) {
+    device.ext_primitive_topology_list_restart.reported = true;
+    device.ext_primitive_topology_list_restart
+        .primitive_topology_list_restart_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
+    device.ext_primitive_topology_list_restart
+        .primitive_topology_list_restart_features_ext.pNext = features.pNext;
+    features.pNext = &device.ext_primitive_topology_list_restart
+                          .primitive_topology_list_restart_features_ext;
+  }
+
+  if (HasExtension("VK_EXT_provoking_vertex", device.extensions)) {
+    device.ext_provoking_vertex.reported = true;
+    device.ext_provoking_vertex.provoking_vertex_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
+    device.ext_provoking_vertex.provoking_vertex_features_ext.pNext =
+        features.pNext;
+    features.pNext = &device.ext_provoking_vertex.provoking_vertex_features_ext;
+  }
+
+  if (HasExtension("VK_KHR_index_type_uint8", device.extensions)) {
+    device.khr_index_type_uint8.reported = true;
+    device.khr_index_type_uint8.index_type_uint8_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES;
+    device.khr_index_type_uint8.index_type_uint8_features_khr.pNext =
+        features.pNext;
+    features.pNext = &device.khr_index_type_uint8.index_type_uint8_features_khr;
+  }
+
+  if (HasExtension("VK_EXT_index_type_uint8", device.extensions)) {
+    device.ext_index_type_uint8.reported = true;
+    device.ext_index_type_uint8.index_type_uint8_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES;
+    device.ext_index_type_uint8.index_type_uint8_features_ext.pNext =
+        features.pNext;
+    features.pNext = &device.ext_index_type_uint8.index_type_uint8_features_ext;
+  }
+
+  if (HasExtension("VK_KHR_vertex_attribute_divisor", device.extensions)) {
+    device.khr_vertex_attribute_divisor.reported = true;
+    device.khr_vertex_attribute_divisor.vertex_attribute_divisor_features_khr
+        .sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES;
+    device.khr_vertex_attribute_divisor.vertex_attribute_divisor_features_khr
+        .pNext = features.pNext;
+    features.pNext = &device.khr_vertex_attribute_divisor
+                          .vertex_attribute_divisor_features_khr;
+  }
+
+  if (HasExtension("VK_EXT_vertex_attribute_divisor", device.extensions)) {
+    device.ext_vertex_attribute_divisor.reported = true;
+    device.ext_vertex_attribute_divisor.vertex_attribute_divisor_features_ext
+        .sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES;
+    device.ext_vertex_attribute_divisor.vertex_attribute_divisor_features_ext
+        .pNext = features.pNext;
+    features.pNext = &device.ext_vertex_attribute_divisor
+                          .vertex_attribute_divisor_features_ext;
+  }
+
+  if (HasExtension("VK_EXT_transform_feedback", device.extensions)) {
+    device.ext_transform_feedback.reported = true;
+    device.ext_transform_feedback.transform_feedback_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
+    device.ext_transform_feedback.transform_feedback_features_ext.pNext =
+        features.pNext;
+    features.pNext =
+        &device.ext_transform_feedback.transform_feedback_features_ext;
+  }
+
+  if (HasExtension("VK_KHR_shader_subgroup_uniform_control_flow",
+                   device.extensions)) {
+    device.khr_shader_subgroup_uniform_control_flow.reported = true;
+    device.khr_shader_subgroup_uniform_control_flow
+        .shader_subgroup_uniform_control_flow_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR;
+    device.khr_shader_subgroup_uniform_control_flow
+        .shader_subgroup_uniform_control_flow_features_khr.pNext =
+        features.pNext;
+    features.pNext = &device.khr_shader_subgroup_uniform_control_flow
+                          .shader_subgroup_uniform_control_flow_features_khr;
+  }
+
+  if (HasExtension("VK_KHR_shader_subgroup_extended_types",
+                   device.extensions)) {
+    device.khr_shader_subgroup_extended_types.reported = true;
+    device.khr_shader_subgroup_extended_types
+        .shader_subgroup_extended_types_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES;
+    device.khr_shader_subgroup_extended_types
+        .shader_subgroup_extended_types_features_khr.pNext = features.pNext;
+    features.pNext = &device.khr_shader_subgroup_extended_types
+                          .shader_subgroup_extended_types_features_khr;
+  }
+
+  if (HasExtension("VK_KHR_8bit_storage", device.extensions)) {
+    device.khr_8bit_storage.reported = true;
+    device.khr_8bit_storage.bit8_storage_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES;
+    device.khr_8bit_storage.bit8_storage_features_khr.pNext = features.pNext;
+    features.pNext = &device.khr_8bit_storage.bit8_storage_features_khr;
+  }
+
+  if (HasExtension("VK_KHR_shader_integer_dot_product", device.extensions)) {
+    device.khr_shader_integer_dot_product.reported = true;
+    device.khr_shader_integer_dot_product
+        .shader_integer_dot_product_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES;
+    device.khr_shader_integer_dot_product
+        .shader_integer_dot_product_features_khr.pNext = features.pNext;
+    features.pNext = &device.khr_shader_integer_dot_product
+                          .shader_integer_dot_product_features_khr;
+  }
+
+  if (HasExtension("VK_IMG_relaxed_line_rasterization", device.extensions)) {
+    device.img_relaxed_line_rasterization.reported = true;
+    device.img_relaxed_line_rasterization
+        .relaxed_line_rasterization_features_img.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG;
+    device.img_relaxed_line_rasterization
+        .relaxed_line_rasterization_features_img.pNext = features.pNext;
+    features.pNext = &device.img_relaxed_line_rasterization
+                          .relaxed_line_rasterization_features_img;
+  }
+
+  if (HasExtension("VK_KHR_line_rasterization", device.extensions)) {
+    device.khr_line_rasterization.reported = true;
+    device.khr_line_rasterization.line_rasterization_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES;
+    device.khr_line_rasterization.line_rasterization_features_khr.pNext =
+        features.pNext;
+    features.pNext =
+        &device.khr_line_rasterization.line_rasterization_features_khr;
+  }
+
+  if (HasExtension("VK_EXT_line_rasterization", device.extensions)) {
+    device.ext_line_rasterization.reported = true;
+    device.ext_line_rasterization.line_rasterization_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES;
+    device.ext_line_rasterization.line_rasterization_features_ext.pNext =
+        features.pNext;
+    features.pNext =
+        &device.ext_line_rasterization.line_rasterization_features_ext;
+  }
+
+  if (HasExtension("VK_EXT_primitives_generated_query", device.extensions)) {
+    device.ext_primitives_generated_query.reported = true;
+    device.ext_primitives_generated_query
+        .primitives_generated_query_features_ext.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT;
+    device.ext_primitives_generated_query
+        .primitives_generated_query_features_ext.pNext = features.pNext;
+    features.pNext = &device.ext_primitives_generated_query
+                          .primitives_generated_query_features_ext;
+  }
+
   vkGetPhysicalDeviceFeatures2(physical_device, &features);
   device.features = features.features;
 
@@ -184,20 +386,15 @@
 
     vkGetPhysicalDeviceProperties2(physical_device, &properties);
 
-    device.bit16_storage_features.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
-    device.bit16_storage_features.pNext = features.pNext;
-    features.pNext = &device.bit16_storage_features;
-
     device.multiview_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
     device.multiview_features.pNext = features.pNext;
     features.pNext = &device.multiview_features;
 
-    device.variable_pointer_features.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
-    device.variable_pointer_features.pNext = features.pNext;
-    features.pNext = &device.variable_pointer_features;
+    device.variable_pointers_features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES;
+    device.variable_pointers_features.pNext = features.pNext;
+    features.pNext = &device.variable_pointers_features;
 
     device.protected_memory_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
@@ -210,10 +407,15 @@
     features.pNext = &device.sampler_ycbcr_conversion_features;
 
     device.shader_draw_parameter_features.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
     device.shader_draw_parameter_features.pNext = features.pNext;
     features.pNext = &device.shader_draw_parameter_features;
 
+    device.bit16_storage_features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+    device.bit16_storage_features.pNext = features.pNext;
+    features.pNext = &device.bit16_storage_features;
+
     vkGetPhysicalDeviceFeatures2(physical_device, &features);
 
     VkPhysicalDeviceExternalFenceInfo external_fence_info = {
@@ -261,6 +463,11 @@
   }
 
   if (device.properties.apiVersion >= VK_API_VERSION_1_2) {
+    device.core11.properties.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
+    device.core11.properties.pNext = properties.pNext;
+    properties.pNext = &device.core11.properties;
+
     device.core12.properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
     device.core12.properties.pNext = properties.pNext;
@@ -268,6 +475,11 @@
 
     vkGetPhysicalDeviceProperties2(physical_device, &properties);
 
+    device.core11.features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+    device.core11.features.pNext = features.pNext;
+    features.pNext = &device.core11.features;
+
     device.core12.features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
     device.core12.features.pNext = features.pNext;
@@ -327,7 +539,8 @@
       return VkJsonInstance();
     instance.layers.reserve(count);
     for (auto& layer : layers) {
-      instance.layers.push_back(VkJsonLayer{layer, std::vector<VkExtensionProperties>()});
+      instance.layers.push_back(
+          VkJsonLayer{layer, std::vector<VkExtensionProperties>()});
       if (!EnumerateExtensions(layer.layerName,
                                &instance.layers.back().extensions))
         return VkJsonInstance();