Merge "Migrate ip-up-vpn to Soong"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index eb9a51d..7ef26bf 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/idlcli/
+               cmds/installd/
                cmds/servicemanager/
                include/input/
                include/powermanager/
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 34ccb21..1c3a4f2 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -35,6 +35,8 @@
     chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable
     chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_free/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_process_free/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable
     chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 74dbf4b..a2491e5 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -86,9 +86,11 @@
     shared_libs: [
         "android.hardware.dumpstate@1.0",
         "android.hardware.dumpstate@1.1",
+        "android.hardware.dumpstate-V1-ndk",
         "libziparchive",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcrypto",
         "libcutils",
         "libdebuggerd_client",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba25a5a..77915d5 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -192,7 +192,7 @@
     dprintf(fd, "progress:\n");
     ds_->progress_->Dump(fd, "  ");
     dprintf(fd, "args: %s\n", ds_->options_->args.c_str());
-    dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str());
+    dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode_string.c_str());
     dprintf(fd, "version: %s\n", ds_->version_.c_str());
     dprintf(fd, "bugreport_dir: %s\n", destination.c_str());
     dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str());
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index eab72f4..e97949e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -57,12 +57,15 @@
 #include <utility>
 #include <vector>
 
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
 #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
@@ -89,11 +92,10 @@
 #include "DumpstateService.h"
 #include "dumpstate.h"
 
-using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
-using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
-using ::android::hardware::dumpstate::V1_1::DumpstateMode;
-using ::android::hardware::dumpstate::V1_1::DumpstateStatus;
-using ::android::hardware::dumpstate::V1_1::toString;
+namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0;
+namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1;
+namespace dumpstate_hal_aidl = aidl::android::hardware::dumpstate;
+
 using ::std::literals::chrono_literals::operator""ms;
 using ::std::literals::chrono_literals::operator""s;
 using ::std::placeholders::_1;
@@ -807,7 +809,7 @@
     printf("Bugreport format version: %s\n", version_.c_str());
     printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
            id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
-           options_->args.c_str(), options_->bugreport_mode.c_str());
+           options_->args.c_str(), options_->bugreport_mode_string.c_str());
     printf("\n");
 }
 
@@ -1800,8 +1802,8 @@
     // Add linker configuration directory
     ds.AddDir(LINKERCONFIG_DIR, true);
 
-    /* Dump cgroupfs */
-    ds.AddDir(CGROUPFS_DIR, true);
+    /* Dump frozen cgroupfs */
+    dump_frozen_cgroupfs();
 
     if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
@@ -2199,6 +2201,194 @@
     return RunStatus::OK;
 }
 
+static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl(
+    const Dumpstate::BugreportMode bugreport_mode) {
+    switch (bugreport_mode) {
+        case Dumpstate::BugreportMode::BUGREPORT_FULL:
+            return dumpstate_hal_hidl::DumpstateMode::FULL;
+        case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
+            return dumpstate_hal_hidl::DumpstateMode::INTERACTIVE;
+        case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
+            return dumpstate_hal_hidl::DumpstateMode::REMOTE;
+        case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+            return dumpstate_hal_hidl::DumpstateMode::WEAR;
+        case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
+            return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY;
+        case Dumpstate::BugreportMode::BUGREPORT_WIFI:
+            return dumpstate_hal_hidl::DumpstateMode::WIFI;
+        case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
+            return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
+    }
+    return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
+}
+
+static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAidl(
+    const Dumpstate::BugreportMode bugreport_mode) {
+    switch (bugreport_mode) {
+        case Dumpstate::BugreportMode::BUGREPORT_FULL:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::FULL;
+        case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::INTERACTIVE;
+        case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::REMOTE;
+        case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WEAR;
+        case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY;
+        case Dumpstate::BugreportMode::BUGREPORT_WIFI:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI;
+        case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
+            return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
+    }
+    return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
+}
+
+static void DoDumpstateBoardHidl(
+    const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0,
+    const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds,
+    const Dumpstate::BugreportMode bugreport_mode,
+    const size_t timeout_sec) {
+
+    using ScopedNativeHandle =
+        std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>;
+    ScopedNativeHandle handle(native_handle_create(static_cast<int>(dumpstate_fds.size()), 0),
+                              [](native_handle_t* handle) {
+                                  // we don't close file handle's here
+                                  // via native_handle_close(handle)
+                                  // instead we let dumpstate_fds close the file handles when
+                                  // dumpstate_fds gets destroyed
+                                  native_handle_delete(handle);
+                              });
+    if (handle == nullptr) {
+        MYLOGE("Could not create native_handle for dumpstate HAL\n");
+        return;
+    }
+
+    for (size_t i = 0; i < dumpstate_fds.size(); i++) {
+        handle.get()->data[i] = dumpstate_fds[i].get();
+    }
+
+    // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
+    // implement just 1.0.
+    const char* descriptor_to_kill;
+    using DumpstateBoardTask = std::packaged_task<bool()>;
+    DumpstateBoardTask dumpstate_board_task;
+    sp<dumpstate_hal_hidl::IDumpstateDevice> dumpstate_hal(
+        dumpstate_hal_hidl::IDumpstateDevice::castFrom(dumpstate_hal_1_0));
+    if (dumpstate_hal != nullptr) {
+        MYLOGI("Using IDumpstateDevice v1.1 HIDL HAL");
+
+        dumpstate_hal_hidl::DumpstateMode dumpstate_hal_mode =
+            GetDumpstateHalModeHidl(bugreport_mode);
+
+        descriptor_to_kill = dumpstate_hal_hidl::IDumpstateDevice::descriptor;
+        dumpstate_board_task =
+            DumpstateBoardTask([timeout_sec, dumpstate_hal_mode, dumpstate_hal, &handle]() -> bool {
+                ::android::hardware::Return<dumpstate_hal_hidl::DumpstateStatus> status =
+                    dumpstate_hal->dumpstateBoard_1_1(handle.get(), dumpstate_hal_mode,
+                                                      SEC_TO_MSEC(timeout_sec));
+                if (!status.isOk()) {
+                    MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+                    return false;
+                } else if (status != dumpstate_hal_hidl::DumpstateStatus::OK) {
+                    MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n",
+                           dumpstate_hal_hidl::toString(status).c_str());
+                    return false;
+                }
+                return true;
+            });
+    } else {
+        MYLOGI("Using IDumpstateDevice v1.0 HIDL HAL");
+
+        descriptor_to_kill = dumpstate_hal_hidl_1_0::IDumpstateDevice::descriptor;
+        dumpstate_board_task = DumpstateBoardTask([dumpstate_hal_1_0, &handle]() -> bool {
+            ::android::hardware::Return<void> status =
+                dumpstate_hal_1_0->dumpstateBoard(handle.get());
+            if (!status.isOk()) {
+                MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+                return false;
+            }
+            return true;
+        });
+    }
+    auto result = dumpstate_board_task.get_future();
+    std::thread(std::move(dumpstate_board_task)).detach();
+
+    if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
+        MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec);
+        if (!android::base::SetProperty(
+                "ctl.interface_restart",
+                android::base::StringPrintf("%s/default", descriptor_to_kill))) {
+            MYLOGE("Couldn't restart dumpstate HAL\n");
+        }
+    }
+    // Wait some time for init to kill dumpstate vendor HAL
+    constexpr size_t killing_timeout_sec = 10;
+    if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
+        MYLOGE(
+            "killing dumpstateBoard timed out after %zus, continue and "
+            "there might be racing in content\n",
+            killing_timeout_sec);
+    }
+}
+
+static void DoDumpstateBoardAidl(
+    const std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal,
+    const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds,
+    const Dumpstate::BugreportMode bugreport_mode, const size_t timeout_sec) {
+    MYLOGI("Using IDumpstateDevice AIDL HAL");
+
+    const char* descriptor_to_kill;
+    using DumpstateBoardTask = std::packaged_task<bool()>;
+    DumpstateBoardTask dumpstate_board_task;
+    dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode dumpstate_hal_mode =
+        GetDumpstateHalModeAidl(bugreport_mode);
+
+    descriptor_to_kill = dumpstate_hal_aidl::IDumpstateDevice::descriptor;
+    dumpstate_board_task = DumpstateBoardTask([dumpstate_hal, &dumpstate_fds, dumpstate_hal_mode,
+                                               timeout_sec]() -> bool {
+        auto status = dumpstate_hal->dumpstateBoard(dumpstate_fds, dumpstate_hal_mode, timeout_sec);
+
+        if (!status.isOk()) {
+            MYLOGE("dumpstateBoard failed: %s\n", status.getDescription().c_str());
+            return false;
+        }
+        return true;
+    });
+    auto result = dumpstate_board_task.get_future();
+    std::thread(std::move(dumpstate_board_task)).detach();
+
+    if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
+        MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec);
+        if (!android::base::SetProperty(
+                "ctl.interface_restart",
+                android::base::StringPrintf("%s/default", descriptor_to_kill))) {
+            MYLOGE("Couldn't restart dumpstate HAL\n");
+        }
+    }
+    // Wait some time for init to kill dumpstate vendor HAL
+    constexpr size_t killing_timeout_sec = 10;
+    if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
+        MYLOGE(
+            "killing dumpstateBoard timed out after %zus, continue and "
+            "there might be racing in content\n",
+            killing_timeout_sec);
+    }
+}
+
+static std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> GetDumpstateBoardAidlService() {
+    const std::string aidl_instance_name =
+        std::string(dumpstate_hal_aidl::IDumpstateDevice::descriptor) + "/default";
+
+    if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) {
+        return nullptr;
+    }
+
+    ndk::SpAIBinder dumpstateBinder(AServiceManager_waitForService(aidl_instance_name.c_str()));
+
+    return dumpstate_hal_aidl::IDumpstateDevice::fromBinder(dumpstateBinder);
+}
+
 void Dumpstate::DumpstateBoard(int out_fd) {
     dprintf(out_fd, "========================================================\n");
     dprintf(out_fd, "== Board\n");
@@ -2220,8 +2410,7 @@
     if (mount_debugfs) {
         RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
                    AS_ROOT_20);
-        RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"},
-                   AS_ROOT_20);
+        RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, AS_ROOT_20);
     }
 
     std::vector<std::string> paths;
@@ -2233,23 +2422,31 @@
             std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
     }
 
-    sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService());
-    if (dumpstate_device_1_0 == nullptr) {
-        MYLOGE("No IDumpstateDevice implementation\n");
+    // get dumpstate HAL AIDL implementation
+    std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal_handle_aidl(
+        GetDumpstateBoardAidlService());
+    if (dumpstate_hal_handle_aidl == nullptr) {
+        MYLOGI("No IDumpstateDevice AIDL implementation\n");
+    }
+
+    // get dumpstate HAL HIDL implementation, only if AIDL HAL implementation not found
+    sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_handle_hidl_1_0 = nullptr;
+    if (dumpstate_hal_handle_aidl == nullptr) {
+        dumpstate_hal_handle_hidl_1_0 = dumpstate_hal_hidl_1_0::IDumpstateDevice::getService();
+        if (dumpstate_hal_handle_hidl_1_0 == nullptr) {
+            MYLOGI("No IDumpstateDevice HIDL implementation\n");
+        }
+    }
+
+    // if neither HIDL nor AIDL implementation found, then return
+    if (dumpstate_hal_handle_hidl_1_0 == nullptr && dumpstate_hal_handle_aidl == nullptr) {
+        MYLOGE("Could not find IDumpstateDevice implementation\n");
         return;
     }
 
-    using ScopedNativeHandle =
-            std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>;
-    ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0),
-                              [](native_handle_t* handle) {
-                                  native_handle_close(handle);
-                                  native_handle_delete(handle);
-                              });
-    if (handle == nullptr) {
-        MYLOGE("Could not create native_handle\n");
-        return;
-    }
+    // this is used to hold the file descriptors and when this variable goes out of scope
+    // the file descriptors are closed
+    std::vector<::ndk::ScopedFileDescriptor> dumpstate_fds;
 
     // TODO(128270426): Check for consent in between?
     for (size_t i = 0; i < paths.size(); i++) {
@@ -2262,65 +2459,26 @@
             MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno));
             return;
         }
-        handle.get()->data[i] = fd.release();
+
+        dumpstate_fds.emplace_back(fd.release());
+        // we call fd.release() here to make sure "fd" does not get closed
+        // after "fd" goes out of scope after this block.
+        // "fd" will be closed when "dumpstate_fds" goes out of scope
+        // i.e. when we exit this function
     }
 
     // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount
     // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we
     // will kill the HAL and grab whatever it dumped in time.
     constexpr size_t timeout_sec = 30;
-    // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
-    // implement just 1.0.
-    const char* descriptor_to_kill;
-    using DumpstateBoardTask = std::packaged_task<bool()>;
-    DumpstateBoardTask dumpstate_board_task;
-    sp<IDumpstateDevice_1_1> dumpstate_device_1_1(
-        IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0));
-    if (dumpstate_device_1_1 != nullptr) {
-        MYLOGI("Using IDumpstateDevice v1.1");
-        descriptor_to_kill = IDumpstateDevice_1_1::descriptor;
-        dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool {
-            ::android::hardware::Return<DumpstateStatus> status =
-                dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode,
-                                                         SEC_TO_MSEC(timeout_sec));
-            if (!status.isOk()) {
-                MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
-                return false;
-            } else if (status != DumpstateStatus::OK) {
-                MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str());
-                return false;
-            }
-            return true;
-        });
-    } else {
-        MYLOGI("Using IDumpstateDevice v1.0");
-        descriptor_to_kill = IDumpstateDevice_1_0::descriptor;
-        dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool {
-            ::android::hardware::Return<void> status =
-                dumpstate_device_1_0->dumpstateBoard(handle.get());
-            if (!status.isOk()) {
-                MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
-                return false;
-            }
-            return true;
-        });
-    }
-    auto result = dumpstate_board_task.get_future();
-    std::thread(std::move(dumpstate_board_task)).detach();
 
-    if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
-        MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec);
-        if (!android::base::SetProperty(
-                "ctl.interface_restart",
-                android::base::StringPrintf("%s/default", descriptor_to_kill))) {
-            MYLOGE("Couldn't restart dumpstate HAL\n");
-        }
-    }
-    // Wait some time for init to kill dumpstate vendor HAL
-    constexpr size_t killing_timeout_sec = 10;
-    if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
-        MYLOGE("killing dumpstateBoard timed out after %zus, continue and "
-               "there might be racing in content\n", killing_timeout_sec);
+    if (dumpstate_hal_handle_aidl != nullptr) {
+        DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode,
+                             timeout_sec);
+    } else if (dumpstate_hal_handle_hidl_1_0 != nullptr) {
+        // run HIDL HAL only if AIDL HAL not found
+        DoDumpstateBoardHidl(dumpstate_hal_handle_hidl_1_0, dumpstate_fds, options_->bugreport_mode,
+                             timeout_sec);
     }
 
     if (mount_debugfs) {
@@ -2333,9 +2491,8 @@
     auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
     for (size_t i = 0; i < paths.size(); i++) {
         struct stat s;
-        if (fstat(handle.get()->data[i], &s) == -1) {
-            MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(),
-                   strerror(errno));
+        if (fstat(dumpstate_fds[i].get(), &s) == -1) {
+            MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno));
             file_sizes[i] = -1;
             continue;
         }
@@ -2574,40 +2731,35 @@
                                bool is_screenshot_requested) {
     // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for
     // default system screenshots.
-    options->bugreport_mode = ModeToString(mode);
+    options->bugreport_mode = mode;
+    options->bugreport_mode_string = ModeToString(mode);
     switch (mode) {
         case Dumpstate::BugreportMode::BUGREPORT_FULL:
             options->do_screenshot = is_screenshot_requested;
-            options->dumpstate_hal_mode = DumpstateMode::FULL;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
             // Currently, the dumpstate binder is only used by Shell to update progress.
             options->do_progress_updates = true;
             options->do_screenshot = is_screenshot_requested;
-            options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
             options->do_vibrate = false;
             options->is_remote_mode = true;
             options->do_screenshot = false;
-            options->dumpstate_hal_mode = DumpstateMode::REMOTE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
             options->do_progress_updates = true;
             options->do_screenshot = is_screenshot_requested;
-            options->dumpstate_hal_mode = DumpstateMode::WEAR;
             break;
         // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
         case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
             options->telephony_only = true;
             options->do_progress_updates = true;
             options->do_screenshot = false;
-            options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WIFI:
             options->wifi_only = true;
             options->do_screenshot = false;
-            options->dumpstate_hal_mode = DumpstateMode::WIFI;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
             break;
@@ -2618,13 +2770,14 @@
     MYLOGI(
         "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d "
         "is_remote_mode: %d show_header_only: %d telephony_only: %d "
-        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
+        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s "
         "limited_only: %d args: %s\n",
         options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket,
         options.do_screenshot, options.is_remote_mode, options.show_header_only,
         options.telephony_only, options.wifi_only,
-        options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
-        toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
+        options.do_progress_updates, options.bugreport_fd.get(),
+        options.bugreport_mode_string.c_str(),
+        options.limited_only, options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2838,7 +2991,7 @@
     }
 
     MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
-           id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
+           id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str());
 
     do_early_screenshot_ = options_->do_progress_updates;
 
@@ -4016,6 +4169,63 @@
     fclose(fp);
 }
 
+void dump_frozen_cgroupfs(const char *dir, int level,
+        int (*dump_from_fd)(const char* title, const char* path, int fd)) {
+    DIR *dirp;
+    struct dirent *d;
+    char *newpath = nullptr;
+
+    dirp = opendir(dir);
+    if (dirp == nullptr) {
+        MYLOGE("%s: %s\n", dir, strerror(errno));
+        return;
+    }
+
+    for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) {
+        if ((d->d_name[0] == '.')
+         && (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
+          || (d->d_name[1] == '\0'))) {
+            continue;
+        }
+        if (d->d_type == DT_DIR) {
+            asprintf(&newpath, "%s/%s/", dir, d->d_name);
+            if (!newpath) {
+                continue;
+            }
+            if (level == 0 && !strncmp(d->d_name, "uid_", 4)) {
+                dump_frozen_cgroupfs(newpath, 1, dump_from_fd);
+            } else if (level == 1 && !strncmp(d->d_name, "pid_", 4)) {
+                char *freezer = nullptr;
+                asprintf(&freezer, "%s/%s", newpath, "cgroup.freeze");
+                if (freezer) {
+                    FILE* fp = fopen(freezer, "r");
+                    if (fp != NULL) {
+                        int frozen;
+                        fscanf(fp, "%d", &frozen);
+                        if (frozen > 0) {
+                            dump_files("", newpath, skip_none, dump_from_fd);
+                        }
+                        fclose(fp);
+                    }
+                    free(freezer);
+                }
+            }
+        }
+    }
+    closedir(dirp);
+}
+
+void dump_frozen_cgroupfs() {
+    if (!ds.IsZipping()) {
+        MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n");
+        return;
+    }
+    MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR);
+    DurationReporter duration_reporter("FROZEN CGROUPFS");
+    if (PropertiesHelper::IsDryRun()) return;
+    dump_frozen_cgroupfs(CGROUPFS_DIR, 0, _add_file_from_fd);
+}
+
 void Dumpstate::UpdateProgress(int32_t delta_sec) {
     if (progress_ == nullptr) {
         MYLOGE("UpdateProgress: progress_ not set\n");
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3722383..852b9a8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -25,6 +25,7 @@
 #include <string>
 #include <vector>
 
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/dumpstate/1.1/types.h>
@@ -400,19 +401,18 @@
         bool limited_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
-        // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
+        // this is used to derive dumpstate HAL bug report mode
         // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
         // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
-        ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
-            ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
+        BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
         // File descriptor to output zip file. Takes precedence over out_dir.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
         android::base::unique_fd screenshot_fd;
         // Custom output directory.
         std::string out_dir;
-        // Bugreport mode of the bugreport.
-        std::string bugreport_mode;
+        // Bugreport mode of the bugreport as a string
+        std::string bugreport_mode_string;
         // Command-line arguments as string
         std::string args;
         // Notification title and description
@@ -637,6 +637,9 @@
 /* Prints the contents of all the routing tables, both IPv4 and IPv6. */
 void dump_route_tables();
 
+/* Dump subdirectories of cgroupfs if the corresponding process is frozen */
+void dump_frozen_cgroupfs();
+
 /* Play a sound via Stagefright */
 void play_sound(const char *path);
 
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index db508b5..42beb2b 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -33,6 +33,7 @@
 #include <unistd.h>
 #include <thread>
 
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -47,6 +48,7 @@
 namespace os {
 namespace dumpstate {
 
+using DumpstateDeviceAidl = ::aidl::android::hardware::dumpstate::IDumpstateDevice;
 using ::android::hardware::dumpstate::V1_1::DumpstateMode;
 using ::testing::EndsWith;
 using ::testing::Eq;
@@ -186,7 +188,6 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.limited_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbBugreport) {
@@ -210,7 +211,6 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) {
@@ -234,13 +234,11 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.limited_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -256,7 +254,6 @@
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -272,7 +269,6 @@
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_screenshot);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
 
     // Other options retain default values
     EXPECT_FALSE(options_.progress_updates_to_socket);
@@ -286,7 +282,7 @@
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
     EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_progress_updates);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
+
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -302,7 +298,6 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.telephony_only);
     EXPECT_TRUE(options_.do_progress_updates);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -317,7 +312,6 @@
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.wifi_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -354,7 +348,6 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.stream_to_socket);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeDefaultBugReport) {
@@ -371,7 +364,6 @@
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -408,7 +400,6 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.limited_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial2) {
@@ -436,7 +427,6 @@
     EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.limited_only);
-    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeHelp) {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 3f180d9..00babc3 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -28,6 +28,7 @@
         "dexopt.cpp",
         "execv_helper.cpp",
         "globals.cpp",
+        "restorable_file.cpp",
         "run_dex2oat.cpp",
         "unique_file.cpp",
         "utils.cpp",
@@ -45,6 +46,7 @@
         "libprocessgroup",
         "libselinux",
         "libutils",
+        "libziparchive",
         "server_configurable_flags",
     ],
     static_libs: [
@@ -79,7 +81,7 @@
         "-cert-err58-cpp",
     ],
     tidy_flags: [
-        "-warnings-as-errors=clang-analyzer-security*,cert-*"
+        "-warnings-as-errors=clang-analyzer-security*,cert-*",
     ],
 }
 
@@ -131,7 +133,10 @@
         "unique_file.cpp",
         "execv_helper.cpp",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "server_configurable_flags",
@@ -169,7 +174,7 @@
 
     // Needs to be wherever installd is as it's execed by
     // installd.
-    required: [ "migrate_legacy_obb_data.sh" ],
+    required: ["migrate_legacy_obb_data.sh"],
 }
 
 // OTA chroot tool
@@ -193,7 +198,7 @@
         "libutils",
     ],
     required: [
-      "apexd"
+        "apexd",
     ],
 }
 
@@ -212,7 +217,7 @@
     name: "libotapreoptparameters",
     cflags: [
         "-Wall",
-        "-Werror"
+        "-Werror",
     ],
 
     srcs: ["otapreopt_parameters.cpp"],
@@ -236,7 +241,7 @@
     name: "otapreopt",
     cflags: [
         "-Wall",
-        "-Werror"
+        "-Werror",
     ],
 
     srcs: [
@@ -245,6 +250,7 @@
         "globals.cpp",
         "otapreopt.cpp",
         "otapreopt_utils.cpp",
+        "restorable_file.cpp",
         "run_dex2oat.cpp",
         "unique_file.cpp",
         "utils.cpp",
@@ -267,6 +273,7 @@
         "libprocessgroup",
         "libselinux",
         "libutils",
+        "libziparchive",
         "server_configurable_flags",
     ],
 }
@@ -294,5 +301,5 @@
 // Script to migrate legacy obb data.
 sh_binary {
     name: "migrate_legacy_obb_data.sh",
-    src: "migrate_legacy_obb_data.sh"
+    src: "migrate_legacy_obb_data.sh",
 }
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index f35abda..c3256fc 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,6 +77,8 @@
 #define LOG_TAG "installd"
 #endif
 
+// #define GRANULAR_LOCKS
+
 using android::base::ParseUint;
 using android::base::StringPrintf;
 using std::endl;
@@ -265,6 +267,104 @@
     }                                                       \
 }
 
+#ifdef GRANULAR_LOCKS
+
+/**
+ * This class obtains in constructor and keeps the local strong pointer to the RefLock.
+ * On destruction, it checks if there are any other strong pointers, and remove the map entry if
+ * this was the last one.
+ */
+template <class Key, class Mutex>
+struct LocalLockHolder {
+    using WeakPointer = std::weak_ptr<Mutex>;
+    using StrongPointer = std::shared_ptr<Mutex>;
+    using Map = std::unordered_map<Key, WeakPointer>;
+    using MapLock = std::recursive_mutex;
+
+    LocalLockHolder(Key key, Map& map, MapLock& mapLock)
+          : mKey(std::move(key)), mMap(map), mMapLock(mapLock) {
+        std::lock_guard lock(mMapLock);
+        auto& weakPtr = mMap[mKey];
+
+        // Check if the RefLock is still alive.
+        mRefLock = weakPtr.lock();
+        if (!mRefLock) {
+            // Create a new lock.
+            mRefLock = std::make_shared<Mutex>();
+            weakPtr = mRefLock;
+        }
+    }
+    LocalLockHolder(LocalLockHolder&& other) noexcept
+          : mKey(std::move(other.mKey)),
+            mMap(other.mMap),
+            mMapLock(other.mMapLock),
+            mRefLock(std::move(other.mRefLock)) {
+        other.mRefLock.reset();
+    }
+    ~LocalLockHolder() {
+        if (!mRefLock) {
+            return;
+        }
+
+        std::lock_guard lock(mMapLock);
+        // Clear the strong pointer.
+        mRefLock.reset();
+        auto found = mMap.find(mKey);
+        if (found == mMap.end()) {
+            return;
+        }
+        const auto& weakPtr = found->second;
+        // If this was the last pointer then it's ok to remove the map entry.
+        if (weakPtr.expired()) {
+            mMap.erase(found);
+        }
+    }
+
+    void lock() { mRefLock->lock(); }
+    void unlock() { mRefLock->unlock(); }
+    void lock_shared() { mRefLock->lock_shared(); }
+    void unlock_shared() { mRefLock->unlock_shared(); }
+
+private:
+    Key mKey;
+    Map& mMap;
+    MapLock& mMapLock;
+    StrongPointer mRefLock;
+};
+
+using UserLock = LocalLockHolder<userid_t, std::shared_mutex>;
+using UserWriteLockGuard = std::unique_lock<UserLock>;
+using UserReadLockGuard = std::shared_lock<UserLock>;
+
+using PackageLock = LocalLockHolder<std::string, std::recursive_mutex>;
+using PackageLockGuard = std::lock_guard<PackageLock>;
+
+#define LOCK_USER()                                     \
+    UserLock localUserLock(userId, mUserIdLock, mLock); \
+    UserWriteLockGuard userLock(localUserLock)
+
+#define LOCK_USER_READ()                                \
+    UserLock localUserLock(userId, mUserIdLock, mLock); \
+    UserReadLockGuard userLock(localUserLock)
+
+#define LOCK_PACKAGE()                                                  \
+    PackageLock localPackageLock(packageName, mPackageNameLock, mLock); \
+    PackageLockGuard packageLock(localPackageLock)
+
+#define LOCK_PACKAGE_USER() \
+    LOCK_USER_READ();       \
+    LOCK_PACKAGE()
+
+#else
+
+#define LOCK_USER() std::lock_guard lock(mLock)
+#define LOCK_PACKAGE() std::lock_guard lock(mLock)
+#define LOCK_PACKAGE_USER() \
+    (void)userId;           \
+    std::lock_guard lock(mLock)
+
+#endif // GRANULAR_LOCKS
+
 }  // namespace
 
 status_t InstalldNativeService::start() {
@@ -288,8 +388,6 @@
         return PERMISSION_DENIED;
     }
 
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
     {
         std::lock_guard<std::recursive_mutex> lock(mMountsLock);
         dprintf(fd, "Storage mounts:\n");
@@ -321,10 +419,17 @@
     int res = 0;
     char* before = nullptr;
     char* after = nullptr;
+    if (!existing) {
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+                SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+            PLOG(ERROR) << "Failed recursive restorecon for " << path;
+            goto fail;
+        }
+        return res;
+    }
 
     // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
     // libselinux. Not needed here.
-
     if (lgetfilecon(path.c_str(), &before) < 0) {
         PLOG(ERROR) << "Failed before getfilecon for " << path;
         goto fail;
@@ -361,12 +466,6 @@
     return res;
 }
 
-static int restorecon_app_data_lazy(const std::string& parent, const char* name,
-        const std::string& seInfo, uid_t uid, bool existing) {
-    return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
-            existing);
-}
-
 static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
     if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
         PLOG(ERROR) << "Failed to prepare " << path;
@@ -512,8 +611,14 @@
         int32_t uid, int32_t* previousUid, int32_t cacheGid,
         const std::string& seInfo, mode_t targetMode) {
     struct stat st{};
-    bool existing = (stat(path.c_str(), &st) == 0);
-    if (existing) {
+    bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
+
+    auto cache_path = StringPrintf("%s/%s", path.c_str(), "cache");
+    auto code_cache_path = StringPrintf("%s/%s", path.c_str(), "code_cache");
+    bool cache_exists = (access(cache_path.c_str(), F_OK) == 0);
+    bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0);
+
+    if (parent_dir_exists) {
         if (*previousUid < 0) {
             // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner
             // of the directory as previousUid. This is required because it is not always possible
@@ -527,6 +632,7 @@
         }
     }
 
+    // Prepare only the parent app directory
     if (prepare_app_dir(path, targetMode, uid) ||
             prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
             prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
@@ -534,23 +640,33 @@
     }
 
     // Consider restorecon over contents if label changed
-    if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
-            restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
-            restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+    if (restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) {
         return error("Failed to restorecon " + path);
     }
 
+    // If the parent dir exists, the restorecon would already have been done
+    // as a part of the recursive restorecon above
+    if (parent_dir_exists && !cache_exists
+            && restorecon_app_data_lazy(cache_path, seInfo, uid, false)) {
+        return error("Failed to restorecon " + cache_path);
+    }
+
+    // If the parent dir exists, the restorecon would already have been done
+    // as a part of the recursive restorecon above
+    if (parent_dir_exists && !code_cache_exists
+            && restorecon_app_data_lazy(code_cache_path, seInfo, uid, false)) {
+        return error("Failed to restorecon " + code_cache_path);
+    }
     return ok();
 }
 
-binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
-        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
-        int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
-        int64_t* _aidl_return) {
+binder::Status InstalldNativeService::createAppDataLocked(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
+        int32_t targetSdkVersion, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
@@ -619,10 +735,22 @@
 }
 
 binder::Status InstalldNativeService::createAppData(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
+        int32_t targetSdkVersion, int64_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    LOCK_PACKAGE_USER();
+    return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo,
+                               targetSdkVersion, _aidl_return);
+}
+
+binder::Status InstalldNativeService::createAppData(
         const android::os::CreateAppDataArgs& args,
         android::os::CreateAppDataResult* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // Locking is performed depeer in the callstack.
 
     int64_t ceDataInode = -1;
     auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
@@ -637,6 +765,7 @@
         const std::vector<android::os::CreateAppDataArgs>& args,
         std::vector<android::os::CreateAppDataResult>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    // Locking is performed depeer in the callstack.
 
     std::vector<android::os::CreateAppDataResult> results;
     for (const auto &arg : args) {
@@ -653,7 +782,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
@@ -697,7 +826,7 @@
         const std::string& profileName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     binder::Status res = ok();
     if (!clear_primary_reference_profile(packageName, profileName)) {
@@ -714,7 +843,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
@@ -811,7 +940,7 @@
 binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     binder::Status res = ok();
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
@@ -831,7 +960,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
@@ -902,15 +1031,15 @@
         int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
-    for (auto user : get_known_users(uuid_)) {
+    for (auto userId : get_known_users(uuid_)) {
+        LOCK_USER();
         ATRACE_BEGIN("fixup user");
         FTS* fts;
         FTSENT* p;
-        auto ce_path = create_data_user_ce_path(uuid_, user);
-        auto de_path = create_data_user_de_path(uuid_, user);
+        auto ce_path = create_data_user_ce_path(uuid_, userId);
+        auto de_path = create_data_user_de_path(uuid_, userId);
         char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr };
         if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
             return error("Failed to fts_open");
@@ -1002,29 +1131,28 @@
 }
 
 static int32_t copy_directory_recursive(const char* from, const char* to) {
-    char *argv[] = {
-        (char*) kCpPath,
-        (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-        (char*) "-p", /* preserve timestamps, ownership, and permissions */
-        (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-        (char*) "-P", /* Do not follow symlinks [default] */
-        (char*) "-d", /* don't dereference symlinks */
-        (char*) from,
-        (char*) to
-    };
+    char* argv[] =
+            {(char*)kCpPath,
+             (char*)"-F", /* delete any existing destination file first (--remove-destination) */
+             (char*)"--preserve=mode,ownership,timestamps,xattr", /* preserve properties */
+             (char*)"-R", /* recurse into subdirectories (DEST must be a directory) */
+             (char*)"-P", /* Do not follow symlinks [default] */
+             (char*)"-d", /* don't dereference symlinks */
+             (char*)from,
+             (char*)to};
 
     LOG(DEBUG) << "Copying " << from << " to " << to;
     return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
 }
 
-binder::Status InstalldNativeService::snapshotAppData(
-        const std::optional<std::string>& volumeUuid,
-        const std::string& packageName, int32_t user, int32_t snapshotId,
-        int32_t storageFlags, int64_t* _aidl_return) {
+binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::string>& volumeUuid,
+                                                      const std::string& packageName,
+                                                      int32_t userId, int32_t snapshotId,
+                                                      int32_t storageFlags, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
@@ -1037,19 +1165,19 @@
     bool clear_ce_on_exit = false;
     bool clear_de_on_exit = false;
 
-    auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name,
-            &snapshotId] {
+    auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &userId, &package_name,
+                    &snapshotId] {
         if (clear_de_on_exit) {
-            auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId,
-                    package_name);
+            auto to = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                                package_name);
             if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
                 LOG(WARNING) << "Failed to delete app data snapshot: " << to;
             }
         }
 
         if (clear_ce_on_exit) {
-            auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId,
-                    package_name);
+            auto to = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                                package_name);
             if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
                 LOG(WARNING) << "Failed to delete app data snapshot: " << to;
             }
@@ -1059,10 +1187,11 @@
     auto scope_guard = android::base::make_scope_guard(deleter);
 
     if (storageFlags & FLAG_STORAGE_DE) {
-        auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
-        auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
-        auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
-            snapshotId, package_name);
+        auto from = create_data_user_de_package_path(volume_uuid, userId, package_name);
+        auto to = create_data_misc_de_rollback_path(volume_uuid, userId, snapshotId);
+        auto rollback_package_path =
+                create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                          package_name);
 
         int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
         if (rc != 0) {
@@ -1086,15 +1215,15 @@
     }
 
     // The app may not have any data at all, in which case it's OK to skip here.
-    auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
+    auto from_ce = create_data_user_ce_package_path(volume_uuid, userId, package_name);
     if (access(from_ce.c_str(), F_OK) != 0) {
         LOG(INFO) << "Missing source " << from_ce;
         return ok();
     }
 
     // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set.
-    binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user,
-            storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
+    binder::Status clear_cache_result =
+            clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
     if (!clear_cache_result.isOk()) {
         // It should be fine to continue snapshot if we for some reason failed
         // to clear cache.
@@ -1102,8 +1231,9 @@
     }
 
     // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set.
-    binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user,
-            storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0);
+    binder::Status clear_code_cache_result =
+            clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY,
+                         0);
     if (!clear_code_cache_result.isOk()) {
         // It should be fine to continue snapshot if we for some reason failed
         // to clear code_cache.
@@ -1111,10 +1241,11 @@
     }
 
     if (storageFlags & FLAG_STORAGE_CE) {
-        auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
-        auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
-        auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
-            snapshotId, package_name);
+        auto from = create_data_user_ce_package_path(volume_uuid, userId, package_name);
+        auto to = create_data_misc_ce_rollback_path(volume_uuid, userId, snapshotId);
+        auto rollback_package_path =
+                create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                          package_name);
 
         int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
         if (rc != 0) {
@@ -1133,8 +1264,9 @@
             return res;
         }
         if (_aidl_return != nullptr) {
-            auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
-                    snapshotId, package_name);
+            auto ce_snapshot_path =
+                    create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                              package_name);
             rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
             if (rc != 0) {
                 res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
@@ -1149,20 +1281,20 @@
 
 binder::Status InstalldNativeService::restoreAppDataSnapshot(
         const std::optional<std::string>& volumeUuid, const std::string& packageName,
-        const int32_t appId, const std::string& seInfo, const int32_t user,
+        const int32_t appId, const std::string& seInfo, const int32_t userId,
         const int32_t snapshotId, int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
 
-    auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
-            user, snapshotId, package_name);
-    auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
-            user, snapshotId, package_name);
+    auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                             package_name);
+    auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                             package_name);
 
     const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) &&
         (access(from_ce.c_str(), F_OK) == 0);
@@ -1182,14 +1314,14 @@
     // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot
     // can only be called when user unlocks the phone, meaning that CE user data
     // is decrypted.
-    binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags,
-            0 /* ceDataInode */);
+    binder::Status res =
+            clearAppData(volumeUuid, packageName, userId, storageFlags, 0 /* ceDataInode */);
     if (!res.isOk()) {
         return res;
     }
 
     if (needs_ce_rollback) {
-        auto to_ce = create_data_user_ce_path(volume_uuid, user);
+        auto to_ce = create_data_user_ce_path(volume_uuid, userId);
         int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str());
         if (rc != 0) {
             res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
@@ -1199,11 +1331,11 @@
     }
 
     if (needs_de_rollback) {
-        auto to_de = create_data_user_de_path(volume_uuid, user);
+        auto to_de = create_data_user_de_path(volume_uuid, userId);
         int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str());
         if (rc != 0) {
             if (needs_ce_rollback) {
-                auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name);
+                auto ce_data = create_data_user_ce_package_path(volume_uuid, userId, package_name);
                 LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data;
                 if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) {
                     LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data;
@@ -1216,24 +1348,24 @@
     }
 
     // Finally, restore the SELinux label on the app data.
-    return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
+    return restoreconAppData(volumeUuid, packageName, userId, storageFlags, appId, seInfo);
 }
 
 binder::Status InstalldNativeService::destroyAppDataSnapshot(
-        const std::optional<std::string> &volumeUuid, const std::string& packageName,
-        const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
+        const std::optional<std::string>& volumeUuid, const std::string& packageName,
+        const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId,
         int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
 
     if (storageFlags & FLAG_STORAGE_DE) {
-        auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
-                user, snapshotId, package_name);
+        auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, userId,
+                                                                          snapshotId, package_name);
 
         int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
         if (res != 0) {
@@ -1242,8 +1374,9 @@
     }
 
     if (storageFlags & FLAG_STORAGE_CE) {
-        auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
-                user, snapshotId, package_name, ceSnapshotInode);
+        auto ce_snapshot_path =
+                create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+                                                          package_name, ceSnapshotInode);
         int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
         if (res != 0) {
             return error(res, "Failed clearing snapshot " + ce_snapshot_path);
@@ -1253,15 +1386,15 @@
 }
 
 binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified(
-        const std::optional<std::string> &volumeUuid, const int32_t user,
+        const std::optional<std::string>& volumeUuid, const int32_t userId,
         const std::vector<int32_t>& retainSnapshotIds) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_USER();
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
 
-    auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user);
+    auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId);
 
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir);
     if (!dir) {
@@ -1279,8 +1412,8 @@
         if (parse_ok &&
                 std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(),
                           snapshot_id) == retainSnapshotIds.end()) {
-            auto rollback_path = create_data_misc_ce_rollback_path(
-                volume_uuid, user, snapshot_id);
+            auto rollback_path =
+                    create_data_misc_ce_rollback_path(volume_uuid, userId, snapshot_id);
             int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */);
             if (res != 0) {
                 return error(res, "Failed clearing snapshot " + rollback_path);
@@ -1298,7 +1431,7 @@
     CHECK_ARGUMENT_UUID(fromUuid);
     CHECK_ARGUMENT_UUID(toUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
     const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
@@ -1326,24 +1459,25 @@
     }
 
     // Copy private data for all known users
-    for (auto user : users) {
+    for (auto userId : users) {
+        LOCK_USER();
 
         // Data source may not exist for all users; that's okay
-        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+        auto from_ce = create_data_user_ce_package_path(from_uuid, userId, package_name);
         if (access(from_ce.c_str(), F_OK) != 0) {
             LOG(INFO) << "Missing source " << from_ce;
             continue;
         }
 
-        if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
-                /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) {
+        if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+                                 appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr)
+                     .isOk()) {
             res = error("Failed to create package target");
             goto fail;
         }
-
         {
-            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_de_path(to_uuid, user);
+            auto from = create_data_user_de_package_path(from_uuid, userId, package_name);
+            auto to = create_data_user_de_path(to_uuid, userId);
 
             int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
@@ -1352,8 +1486,8 @@
             }
         }
         {
-            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_ce_path(to_uuid, user);
+            auto from = create_data_user_ce_package_path(from_uuid, userId, package_name);
+            auto to = create_data_user_ce_path(to_uuid, userId);
 
             int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
@@ -1362,8 +1496,9 @@
             }
         }
 
-        if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                appId, seInfo).isOk()) {
+        if (!restoreconAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+                                     appId, seInfo)
+                     .isOk()) {
             res = error("Failed to restorecon");
             goto fail;
         }
@@ -1381,15 +1516,16 @@
             LOG(WARNING) << "Failed to rollback " << to_app_package_path;
         }
     }
-    for (auto user : users) {
+    for (auto userId : users) {
+        LOCK_USER();
         {
-            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+            auto to = create_data_user_de_package_path(to_uuid, userId, package_name);
             if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
                 LOG(WARNING) << "Failed to rollback " << to;
             }
         }
         {
-            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+            auto to = create_data_user_ce_package_path(to_uuid, userId, package_name);
             if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
                 LOG(WARNING) << "Failed to rollback " << to;
             }
@@ -1402,7 +1538,7 @@
         int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     if (flags & FLAG_STORAGE_DE) {
@@ -1420,7 +1556,7 @@
         int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     binder::Status res = ok();
@@ -1457,7 +1593,9 @@
         int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+#ifndef GRANULAR_LOCKS
+    std::lock_guard lock(mLock);
+#endif // !GRANULAR_LOCKS
 
     auto uuidString = uuid.value_or("");
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -1484,13 +1622,24 @@
 
         // 1. Create trackers for every known UID
         ATRACE_BEGIN("create");
+        const auto users = get_known_users(uuid_);
+#ifdef GRANULAR_LOCKS
+        std::vector<UserLock> userLocks;
+        userLocks.reserve(users.size());
+        std::vector<UserWriteLockGuard> lockGuards;
+        lockGuards.reserve(users.size());
+#endif // GRANULAR_LOCKS
         std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers;
-        for (auto user : get_known_users(uuid_)) {
+        for (auto userId : users) {
+#ifdef GRANULAR_LOCKS
+            userLocks.emplace_back(userId, mUserIdLock, mLock);
+            lockGuards.emplace_back(userLocks.back());
+#endif // GRANULAR_LOCKS
             FTS *fts;
             FTSENT *p;
-            auto ce_path = create_data_user_ce_path(uuid_, user);
-            auto de_path = create_data_user_de_path(uuid_, user);
-            auto media_path = findDataMediaPath(uuid, user) + "/Android/data/";
+            auto ce_path = create_data_user_ce_path(uuid_, userId);
+            auto de_path = create_data_user_de_path(uuid_, userId);
+            auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/";
             char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(),
                     (char*) media_path.c_str(), nullptr };
             if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
@@ -1855,7 +2004,18 @@
     }
     fts_close(fts);
 }
-
+static bool ownsExternalStorage(int32_t appId) {
+    //  Fetch external storage owner appid  and check if it is the same as the
+    //  current appId whose size is calculated
+    struct stat s;
+    auto _picDir = StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str());
+    // check if the stat are present
+    if (stat(_picDir.c_str(), &s) == 0) {
+        // fetch the appId from the uid of the media app
+        return ((int32_t)multiuser_get_app_id(s.st_uid) == appId);
+    }
+    return false;
+}
 binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid,
         const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
         int32_t appId, const std::vector<int64_t>& ceDataInodes,
@@ -1910,8 +2070,10 @@
         calculate_tree_size(obbCodePath, &extStats.codeSize);
     }
     ATRACE_END();
-
-    if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+    // Calculating the app size of the external storage owning app in a manual way, since
+    // calculating it through quota apis also includes external media storage in the app storage
+    // numbers
+    if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) {
         ATRACE_BEGIN("code");
         for (const auto& codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize, -1,
@@ -2341,7 +2503,7 @@
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     }
 #ifdef ENABLE_STORAGE_CRATES
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     auto retVector = std::vector<std::optional<CrateMetadata>>();
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -2387,7 +2549,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
 #ifdef ENABLE_STORAGE_CRATES
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto retVector = std::vector<std::optional<CrateMetadata>>();
@@ -2444,7 +2606,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(codePath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     *_aidl_return = dump_profiles(uid, packageName, profileName, codePath);
     return ok();
@@ -2456,7 +2618,7 @@
         bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
     *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName);
     return ok();
 }
@@ -2466,7 +2628,7 @@
         const std::string& profileName, int* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     *_aidl_return = analyze_primary_profiles(uid, packageName, profileName);
     return ok();
@@ -2477,7 +2639,7 @@
         const std::string& classpath, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath);
     return ok();
@@ -2487,7 +2649,7 @@
         const std::string& profileName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     std::string snapshot = create_snapshot_profile_path(packageName, profileName);
     if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) {
@@ -2518,7 +2680,8 @@
     }
     CHECK_ARGUMENT_PATH(outputPath);
     CHECK_ARGUMENT_PATH(dexMetadataPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    const auto userId = multiuser_get_user_id(uid);
+    LOCK_PACKAGE_USER();
 
     const char* oat_dir = getCStr(outputPath);
     const char* instruction_set = instructionSet.c_str();
@@ -2569,7 +2732,7 @@
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(nativeLibPath32);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
@@ -2660,7 +2823,16 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
+    return restoreconAppDataLocked(uuid, packageName, userId, flags, appId, seInfo);
+}
+
+binder::Status InstalldNativeService::restoreconAppDataLocked(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags, int32_t appId, const std::string& seInfo) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
 
     binder::Status res = ok();
 
@@ -2690,7 +2862,7 @@
         const std::string& instructionSet) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(oatDir);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     const char* oat_dir = oatDir.c_str();
     const char* instruction_set = instructionSet.c_str();
@@ -2715,7 +2887,7 @@
 binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(packageDir);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     if (validate_apk_path(packageDir.c_str())) {
         return error("Invalid path " + packageDir);
@@ -2731,7 +2903,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(fromBase);
     CHECK_ARGUMENT_PATH(toBase);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     const char* relative_path = relativePath.c_str();
     const char* from_base = fromBase.c_str();
@@ -2761,7 +2933,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     const char* apk_path = apkPath.c_str();
     const char* instruction_set = instructionSet.c_str();
@@ -2777,7 +2949,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     const char* apk_path = apkPath.c_str();
     const char* instruction_set = instructionSet.c_str();
@@ -2810,7 +2982,7 @@
         android::base::unique_fd verityInputAshmem, int32_t contentSize) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(filePath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
         return ok();
@@ -2892,7 +3064,7 @@
         const std::vector<uint8_t>& expectedHash) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(filePath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE();
 
     if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
         return ok();
@@ -2931,7 +3103,8 @@
     CHECK_ARGUMENT_UUID(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(dexPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    const auto userId = multiuser_get_user_id(uid);
+    LOCK_PACKAGE_USER();
 
     bool result = android::installd::reconcile_secondary_dex_file(
             dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
@@ -3011,8 +3184,9 @@
 
     const char* uuid_ = uuid->c_str();
 
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
     std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
-    std::lock_guard<std::recursive_mutex> lock(mLock);
     if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
         return error("Failed to create CE mirror");
     }
@@ -3081,8 +3255,9 @@
     std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
 
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
     // Unmount CE storage
-    std::lock_guard<std::recursive_mutex> lock(mLock);
     if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
         if (errno != ENOENT) {
             res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
@@ -3131,7 +3306,7 @@
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(codePath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    LOCK_PACKAGE_USER();
 
     *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath,
         dexMetadata);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 8cfda01..09581bb 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -21,8 +21,9 @@
 #include <inttypes.h>
 #include <unistd.h>
 
-#include <vector>
+#include <shared_mutex>
 #include <unordered_map>
+#include <vector>
 
 #include <android-base/macros.h>
 #include <binder/BinderService.h>
@@ -49,6 +50,11 @@
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
             int64_t* _aidl_return);
+    binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
+                                       const std::string& packageName, int32_t userId,
+                                       int32_t flags, int32_t appId, int32_t previousAppId,
+                                       const std::string& seInfo, int32_t targetSdkVersion,
+                                       int64_t* _aidl_return);
 
     binder::Status createAppData(
             const android::os::CreateAppDataArgs& args,
@@ -60,6 +66,9 @@
     binder::Status restoreconAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             const std::string& seInfo);
+    binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
+                                           const std::string& packageName, int32_t userId,
+                                           int32_t flags, int32_t appId, const std::string& seInfo);
     binder::Status migrateAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags);
     binder::Status clearAppData(const std::optional<std::string>& uuid,
@@ -181,6 +190,8 @@
 
 private:
     std::recursive_mutex mLock;
+    std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
+    std::unordered_map<std::string, std::weak_ptr<std::recursive_mutex>> mPackageNameLock;
 
     std::recursive_mutex mMountsLock;
     std::recursive_mutex mQuotasLock;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f3ec63f..b6f42ad 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -52,14 +52,16 @@
 #include <server_configurable_flags/get_flags.h>
 #include <system/thread_defs.h>
 #include <utils/Mutex.h>
+#include <ziparchive/zip_archive.h>
 
 #include "dexopt.h"
 #include "dexopt_return_codes.h"
 #include "execv_helper.h"
 #include "globals.h"
-#include "installd_deps.h"
 #include "installd_constants.h"
+#include "installd_deps.h"
 #include "otapreopt_utils.h"
+#include "restorable_file.h"
 #include "run_dex2oat.h"
 #include "unique_file.h"
 #include "utils.h"
@@ -308,12 +310,6 @@
     return profile_boot_class_path == "true";
 }
 
-static void UnlinkIgnoreResult(const std::string& path) {
-    if (unlink(path.c_str()) < 0) {
-        PLOG(ERROR) << "Failed to unlink " << path;
-    }
-}
-
 /*
  * Whether dexopt should use a swap file when compiling an APK.
  *
@@ -459,8 +455,8 @@
     });
 }
 
-static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
-        const std::string& location) {
+static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name,
+                                       const std::string& location) {
     std::string profile = create_snapshot_profile_path(package_name, location);
     return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC,  S_IRUSR | S_IWUSR);
 }
@@ -987,42 +983,34 @@
 }
 
 // (re)Creates the app image if needed.
-UniqueFile maybe_open_app_image(const std::string& out_oat_path,
-        bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
-
+RestorableFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image,
+                                    bool is_public, int uid, bool is_secondary_dex) {
     const std::string image_path = create_image_filename(out_oat_path);
     if (image_path.empty()) {
         // Happens when the out_oat_path has an unknown extension.
-        return UniqueFile();
+        return RestorableFile();
     }
 
-    // In case there is a stale image, remove it now. Ignore any error.
-    unlink(image_path.c_str());
-
     // Not enabled, exit.
     if (!generate_app_image) {
-        return UniqueFile();
+        RestorableFile::RemoveAllFiles(image_path);
+        return RestorableFile();
     }
     std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
     if (app_image_format.empty()) {
-        return UniqueFile();
+        RestorableFile::RemoveAllFiles(image_path);
+        return RestorableFile();
     }
-    // Recreate is true since we do not want to modify a mapped image. If the app is
-    // already running and we modify the image file, it can cause crashes (b/27493510).
-    UniqueFile image_file(
-            open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
-            image_path,
-            UnlinkIgnoreResult);
+    // If the app is already running and we modify the image file, it can cause crashes
+    // (b/27493510).
+    RestorableFile image_file = RestorableFile::CreateWritableFile(image_path,
+                                                                   /*permissions*/ 0600);
     if (image_file.fd() < 0) {
         // Could not create application image file. Go on since we can compile without it.
         LOG(ERROR) << "installd could not create '" << image_path
                 << "' for image file during dexopt";
-         // If we have a valid image file path but no image fd, explicitly erase the image file.
-        if (unlink(image_path.c_str()) < 0) {
-            if (errno != ENOENT) {
-                PLOG(ERROR) << "Couldn't unlink image file " << image_path;
-            }
-        }
+        // If we have a valid image file path but cannot create tmp file, reset it.
+        image_file.reset();
     } else if (!set_permissions_and_ownership(
                 image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
@@ -1096,9 +1084,9 @@
 // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
 // out_vdex_wrapper. Returns true for success or false in case of errors.
 bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
-        const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
-        bool profile_guided, UniqueFile* in_vdex_wrapper,
-        UniqueFile* out_vdex_wrapper) {
+                                 const char* instruction_set, bool is_public, int uid,
+                                 bool is_secondary_dex, bool profile_guided,
+                                 UniqueFile* in_vdex_wrapper, RestorableFile* out_vdex_wrapper) {
     CHECK(in_vdex_wrapper != nullptr);
     CHECK(out_vdex_wrapper != nullptr);
     // Open the existing VDEX. We do this before creating the new output VDEX, which will
@@ -1113,6 +1101,14 @@
         return false;
     }
 
+    // Create work file first. All files will be deleted when it fails.
+    *out_vdex_wrapper = RestorableFile::CreateWritableFile(out_vdex_path_str,
+                                                           /*permissions*/ 0644);
+    if (out_vdex_wrapper->fd() < 0) {
+        ALOGE("installd cannot open vdex '%s' during dexopt\n", out_vdex_path_str.c_str());
+        return false;
+    }
+
     bool update_vdex_in_place = false;
     if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
         // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
@@ -1144,41 +1140,19 @@
             (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
             !profile_guided;
         if (update_vdex_in_place) {
+            // dex2oat marks it invalid anyway. So delete it and set work file fd.
+            unlink(in_vdex_path_str.c_str());
             // Open the file read-write to be able to update it.
-            in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
-                                   in_vdex_path_str);
-            if (in_vdex_wrapper->fd() == -1) {
-                // If we failed to open the file, we cannot update it in place.
-                update_vdex_in_place = false;
-            }
+            in_vdex_wrapper->reset(out_vdex_wrapper->fd(), in_vdex_path_str);
+            // Disable auto close for the in wrapper fd (it will be done when destructing the out
+            // wrapper).
+            in_vdex_wrapper->DisableAutoClose();
         } else {
             in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
                                    in_vdex_path_str);
         }
     }
 
-    // If we are updating the vdex in place, we do not need to recreate a vdex,
-    // and can use the same existing one.
-    if (update_vdex_in_place) {
-        // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
-        // have bogus stale vdex files.
-        out_vdex_wrapper->reset(
-              in_vdex_wrapper->fd(),
-              out_vdex_path_str,
-              UnlinkIgnoreResult);
-        // Disable auto close for the in wrapper fd (it will be done when destructing the out
-        // wrapper).
-        in_vdex_wrapper->DisableAutoClose();
-    } else {
-        out_vdex_wrapper->reset(
-              open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-              out_vdex_path_str,
-              UnlinkIgnoreResult);
-        if (out_vdex_wrapper->fd() < 0) {
-            ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
-            return false;
-        }
-    }
     if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
             out_vdex_path_str.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
@@ -1190,16 +1164,13 @@
 }
 
 // Opens the output oat file for the given apk.
-UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
-        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+RestorableFile open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid,
+                                 const char* instruction_set, bool is_secondary_dex) {
     char out_oat_path[PKG_PATH_MAX];
     if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
-        return UniqueFile();
+        return RestorableFile();
     }
-    UniqueFile oat(
-            open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
-            out_oat_path,
-            UnlinkIgnoreResult);
+    RestorableFile oat = RestorableFile::CreateWritableFile(out_oat_path, /*permissions*/ 0644);
     if (oat.fd() < 0) {
         PLOG(ERROR) << "installd cannot open output during dexopt" <<  out_oat_path;
     } else if (!set_permissions_and_ownership(
@@ -1838,6 +1809,7 @@
         if (sec_dex_result == kSecondaryDexOptProcessOk) {
             oat_dir = oat_dir_str.c_str();
             if (dexopt_needed == NO_DEXOPT_NEEDED) {
+                *completed = true;
                 return 0;  // Nothing to do, report success.
             }
         } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) {
@@ -1873,8 +1845,8 @@
     }
 
     // Create the output OAT file.
-    UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
-            instruction_set, is_secondary_dex);
+    RestorableFile out_oat =
+            open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex);
     if (out_oat.fd() < 0) {
         *error_msg = "Could not open out oat file.";
         return -1;
@@ -1882,7 +1854,7 @@
 
     // Open vdex files.
     UniqueFile in_vdex;
-    UniqueFile out_vdex;
+    RestorableFile out_vdex;
     if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
             instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
             &out_vdex)) {
@@ -1918,8 +1890,8 @@
     }
 
     // Create the app image file if needed.
-    UniqueFile out_image = maybe_open_app_image(
-            out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
+    RestorableFile out_image = maybe_open_app_image(out_oat.path(), generate_app_image, is_public,
+                                                    uid, is_secondary_dex);
 
     UniqueFile dex_metadata;
     if (dex_metadata_path != nullptr) {
@@ -1952,30 +1924,18 @@
     LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
 
     RunDex2Oat runner(dex2oat_bin, execv_helper.get());
-    runner.Initialize(out_oat,
-                      out_vdex,
-                      out_image,
-                      in_dex,
-                      in_vdex,
-                      dex_metadata,
-                      reference_profile,
-                      class_loader_context,
-                      join_fds(context_input_fds),
-                      swap_fd.get(),
-                      instruction_set,
-                      compiler_filter,
-                      debuggable,
-                      boot_complete,
-                      for_restore,
-                      target_sdk_version,
-                      enable_hidden_api_checks,
-                      generate_compact_dex,
-                      use_jitzygote_image,
+    runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(),
+                      in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
+                      join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
+                      debuggable, boot_complete, for_restore, target_sdk_version,
+                      enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
                       compilation_reason);
 
     bool cancelled = false;
     pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
     if (cancelled) {
+        *completed = false;
+        reference_profile.DisableCleanup();
         return 0;
     }
     if (pid == 0) {
@@ -2003,6 +1963,7 @@
                 LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled";
                 // cancelled, not an error
                 *completed = false;
+                reference_profile.DisableCleanup();
                 return 0;
             }
             LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"
@@ -2012,13 +1973,42 @@
         }
     }
 
-    // TODO(b/156537504) Implement SWAP of completed files
-    // We've been successful, don't delete output.
-    out_oat.DisableCleanup();
-    out_vdex.DisableCleanup();
-    out_image.DisableCleanup();
+    // dex2oat ran successfully, so profile is safe to keep.
     reference_profile.DisableCleanup();
 
+    // We've been successful, commit work files.
+    // If committing (=renaming tmp to regular) fails, try to restore backup files.
+    // If restoring fails as well, as a last resort, remove all files.
+    if (!out_oat.CreateBackupFile() || !out_vdex.CreateBackupFile() ||
+        !out_image.CreateBackupFile()) {
+        // Renaming failure can mean that the original file may not be accessible from installd.
+        LOG(ERROR) << "Cannot create backup file from existing file, file in wrong state?"
+                   << ", out_oat:" << out_oat.path() << " ,out_vdex:" << out_vdex.path()
+                   << " ,out_image:" << out_image.path();
+        out_oat.ResetAndRemoveAllFiles();
+        out_vdex.ResetAndRemoveAllFiles();
+        out_image.ResetAndRemoveAllFiles();
+        return -1;
+    }
+    if (!out_oat.CommitWorkFile() || !out_vdex.CommitWorkFile() || !out_image.CommitWorkFile()) {
+        LOG(ERROR) << "Cannot commit, out_oat:" << out_oat.path()
+                   << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+        if (!out_oat.RestoreBackupFile() || !out_vdex.RestoreBackupFile() ||
+            !out_image.RestoreBackupFile()) {
+            LOG(ERROR) << "Cannot cancel commit, out_oat:" << out_oat.path()
+                       << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+            // Restoring failed.
+            out_oat.ResetAndRemoveAllFiles();
+            out_vdex.ResetAndRemoveAllFiles();
+            out_image.ResetAndRemoveAllFiles();
+        }
+        return -1;
+    }
+    // Now remove remaining backup files.
+    out_oat.RemoveBackupFile();
+    out_vdex.RemoveBackupFile();
+    out_image.RemoveBackupFile();
+
     *completed = true;
     return 0;
 }
@@ -2562,7 +2552,7 @@
                                         const std::string& classpath) {
     int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
 
-    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+    unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
     if (snapshot_fd < 0) {
         return false;
     }
@@ -2636,7 +2626,7 @@
     }
 
     // Open and create the snapshot profile.
-    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+    unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
 
     // Collect all non empty profiles.
     // The collection will traverse all applications profiles and find the non empty files.
@@ -2738,6 +2728,20 @@
     }
 }
 
+static bool check_profile_exists_in_dexmetadata(const std::string& dex_metadata) {
+    ZipArchiveHandle zip = nullptr;
+    if (OpenArchive(dex_metadata.c_str(), &zip) != 0) {
+        PLOG(ERROR) << "Failed to open dm '" << dex_metadata << "'";
+        return false;
+    }
+
+    ZipEntry64 entry;
+    int result = FindEntry(zip, "primary.prof", &entry);
+    CloseArchive(zip);
+
+    return result != 0 ? false : true;
+}
+
 bool prepare_app_profile(const std::string& package_name,
                          userid_t user_id,
                          appid_t app_id,
@@ -2754,7 +2758,7 @@
     }
 
     // Check if we need to install the profile from the dex metadata.
-    if (!dex_metadata) {
+    if (!dex_metadata || !check_profile_exists_in_dexmetadata(dex_metadata->c_str())) {
         return true;
     }
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6aa32b8..e978e79 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -140,8 +140,8 @@
 
         PrepareEnvironmentVariables();
 
-        if (!EnsureBootImageAndDalvikCache()) {
-            LOG(ERROR) << "Bad boot image.";
+        if (!EnsureDalvikCache()) {
+            LOG(ERROR) << "Bad dalvik cache.";
             return 5;
         }
 
@@ -349,8 +349,8 @@
         }
     }
 
-    // Ensure that we have the right boot image and cache file structures.
-    bool EnsureBootImageAndDalvikCache() const {
+    // Ensure that we have the right cache file structures.
+    bool EnsureDalvikCache() const {
         if (parameters_.instruction_set == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
@@ -376,15 +376,6 @@
             }
         }
 
-        // Check whether we have a boot image.
-        // TODO: check that the files are correct wrt/ jars.
-        std::string preopted_boot_art_path =
-            StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
-        if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
-            PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
-            return false;
-        }
-
         return true;
     }
 
diff --git a/cmds/installd/restorable_file.cpp b/cmds/installd/restorable_file.cpp
new file mode 100644
index 0000000..fd54a23
--- /dev/null
+++ b/cmds/installd/restorable_file.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "restorable_file.h"
+
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace {
+
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+std::string GetTmpFilePath(const std::string& path) {
+    return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix);
+}
+
+std::string GetBackupFilePath(const std::string& path) {
+    return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix);
+}
+
+void UnlinkPossiblyNonExistingFile(const std::string& path) {
+    if (unlink(path.c_str()) < 0) {
+        if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist.
+            PLOG(ERROR) << "Cannot unlink: " << path;
+        }
+    }
+}
+
+// Check if file for the given path exists
+bool FileExists(const std::string& path) {
+    struct stat st;
+    return ::stat(path.c_str(), &st) == 0;
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+RestorableFile::RestorableFile() : RestorableFile(-1, "") {}
+
+RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) {
+    // As cleanup is null, this does not make much difference but use unique_file_ only for closing
+    // tmp file.
+    unique_file_.DisableCleanup();
+}
+
+RestorableFile::~RestorableFile() {
+    reset();
+}
+
+void RestorableFile::reset() {
+    // need to copy before reset clears it.
+    std::string path(unique_file_.path());
+    unique_file_.reset();
+    if (!path.empty()) {
+        UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+    }
+}
+
+bool RestorableFile::CreateBackupFile() {
+    if (path().empty() || !FileExists(path())) {
+        return true;
+    }
+    std::string backup = GetBackupFilePath(path());
+    UnlinkPossiblyNonExistingFile(backup);
+    if (rename(path().c_str(), backup.c_str()) < 0) {
+        PLOG(ERROR) << "Cannot rename " << path() << " to " << backup;
+        return false;
+    }
+    return true;
+}
+
+bool RestorableFile::CommitWorkFile() {
+    std::string path(unique_file_.path());
+    // Keep the path with Commit for debugging purpose.
+    unique_file_.reset(-1, path);
+    if (!path.empty()) {
+        if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) {
+            PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path;
+            // Remove both files as renaming can fail due to the original file as well.
+            UnlinkPossiblyNonExistingFile(path);
+            UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool RestorableFile::RestoreBackupFile() {
+    std::string backup = GetBackupFilePath(path());
+    if (path().empty() || !FileExists(backup)) {
+        return true;
+    }
+    UnlinkPossiblyNonExistingFile(path());
+    if (rename(backup.c_str(), path().c_str()) < 0) {
+        PLOG(ERROR) << "Cannot rename " << backup << " to " << path();
+        return false;
+    }
+    return true;
+}
+
+void RestorableFile::RemoveBackupFile() {
+    UnlinkPossiblyNonExistingFile(GetBackupFilePath(path()));
+}
+
+const UniqueFile& RestorableFile::GetUniqueFile() const {
+    return unique_file_;
+}
+
+void RestorableFile::ResetAndRemoveAllFiles() {
+    std::string path(unique_file_.path());
+    reset();
+    RemoveAllFiles(path);
+}
+
+RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) {
+    std::string tmp_file_path = GetTmpFilePath(path);
+    // If old tmp file exists, delete it.
+    UnlinkPossiblyNonExistingFile(tmp_file_path);
+    int fd = -1;
+    if (!path.empty()) {
+        fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions);
+        if (fd < 0) {
+            PLOG(ERROR) << "Cannot create file: " << tmp_file_path;
+        }
+    }
+    RestorableFile rf(fd, path);
+    return rf;
+}
+
+void RestorableFile::RemoveAllFiles(const std::string& path) {
+    UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+    UnlinkPossiblyNonExistingFile(GetBackupFilePath(path));
+    UnlinkPossiblyNonExistingFile(path);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/restorable_file.h b/cmds/installd/restorable_file.h
new file mode 100644
index 0000000..eda2292
--- /dev/null
+++ b/cmds/installd/restorable_file.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_RESTORABLE_FILE_H
+#define ANDROID_INSTALLD_RESTORABLE_FILE_H
+
+#include <functional>
+#include <string>
+
+#include "unique_file.h"
+
+namespace android {
+namespace installd {
+
+// This is a file abstraction which allows restoring to the original file while temporary work
+// file is updated.
+//
+// Typical flow for this API will be:
+// RestorableFile rf =  RestorableFile::CreateWritableFile(...)
+// write to file using file descriptor acquired from: rf.fd()
+// Make work file into a regular file with: rf.CommitWorkFile()
+// Or throw away the work file by destroying the instance without calling CommitWorkFile().
+// The temporary work file is closed / removed when an instance is destroyed without calling
+// CommitWorkFile(). The original file, if CommitWorkFile() is not called, will be kept.
+//
+// For safer restoration of original file when commit fails, following 3 steps can be taken:
+// 1. CreateBackupFile(): This renames an existing regular file into a separate backup file.
+// 2. CommitWorkFile(): Rename the work file into the regular file.
+// 3. RemoveBackupFile(): Removes the backup file
+// If CommitWorkFile fails, client can call RestoreBackupFile() which will restore regular file from
+// the backup.
+class RestorableFile {
+public:
+    // Creates invalid instance with no fd (=-1) and empty path.
+    RestorableFile();
+    RestorableFile(RestorableFile&& other) = default;
+    ~RestorableFile();
+
+    // Passes all contents of other file into the current file.
+    // Files kept for the current file will be either deleted or committed depending on
+    // CommitWorkFile() and DisableCleanUp() calls made before this.
+    RestorableFile& operator=(RestorableFile&& other) = default;
+
+    // Gets file descriptor for backing work (=temporary) file. If work file does not exist, it will
+    // return -1.
+    int fd() const { return unique_file_.fd(); }
+
+    // Gets the path name for the regular file (not temporary file).
+    const std::string& path() const { return unique_file_.path(); }
+
+    // Closes work file, deletes it and resets all internal states into default states.
+    void reset();
+
+    // Closes work file and closes all files including work file, backup file and regular file.
+    void ResetAndRemoveAllFiles();
+
+    // Creates a backup file by renaming existing regular file. This will return false if renaming
+    // fails. If regular file for renaming does not exist, it will return true.
+    bool CreateBackupFile();
+
+    // Closes existing work file and makes it a regular file.
+    // Note that the work file is closed and fd() will return -1 after this. path() will still
+    // return the original path.
+    // This will return false when committing fails (=cannot rename). Both the regular file and tmp
+    // file will be deleted when it fails.
+    bool CommitWorkFile();
+
+    // Cancels the commit and restores the backup file into the regular one. If renaming fails,
+    // it will return false. This returns true if the backup file does not exist.
+    bool RestoreBackupFile();
+
+    // Removes the backup file.
+    void RemoveBackupFile();
+
+    // Gets UniqueFile with the same path and fd() pointing to the work file.
+    const UniqueFile& GetUniqueFile() const;
+
+    // Creates writable RestorableFile. This involves creating tmp file for writing.
+    static RestorableFile CreateWritableFile(const std::string& path, int permissions);
+
+    // Removes the specified file together with tmp file generated as RestorableFile.
+    static void RemoveAllFiles(const std::string& path);
+
+private:
+    RestorableFile(int value, const std::string& path);
+
+    // Used as a storage for work file fd and path string.
+    UniqueFile unique_file_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_RESTORABLE_FILE_H
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index b661684..51c4589 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -50,10 +50,6 @@
 static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
 static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
 
-// Location of the JIT Zygote image.
-static const char* kJitZygoteImage =
-    "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
-
 std::vector<std::string> SplitBySpaces(const std::string& str) {
     if (str.empty()) {
         return {};
@@ -84,9 +80,9 @@
                             int target_sdk_version,
                             bool enable_hidden_api_checks,
                             bool generate_compact_dex,
-                            bool use_jitzygote_image,
+                            bool use_jitzygote,
                             const char* compilation_reason) {
-    PrepareBootImageFlags(use_jitzygote_image);
+    PrepareBootImageFlags(use_jitzygote);
 
     PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
                           dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,14 +108,14 @@
 
 RunDex2Oat::~RunDex2Oat() {}
 
-void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
-    std::string boot_image;
-    if (use_jitzygote_image) {
-        boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote) {
+    if (use_jitzygote) {
+        // Don't pass a boot image because JIT Zygote should decide which image to use. Typically,
+        // it does not use any boot image on disk.
+        AddArg("--force-jit-zygote");
     } else {
-        boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+        AddArg(MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"));
     }
-    AddArg(boot_image);
 }
 
 void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 475e124..559244f 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -50,13 +50,13 @@
                     int target_sdk_version,
                     bool enable_hidden_api_checks,
                     bool generate_compact_dex,
-                    bool use_jitzygote_image,
+                    bool use_jitzygote,
                     const char* compilation_reason);
 
     void Exec(int exit_code);
 
   protected:
-    void PrepareBootImageFlags(bool use_jitzygote_image);
+    void PrepareBootImageFlags(bool use_jitzygote);
     void PrepareInputFileFlags(const UniqueFile& output_oat,
                                const UniqueFile& output_vdex,
                                const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 0a638cd..2a8135a 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -114,7 +114,7 @@
         int target_sdk_version = 0;
         bool enable_hidden_api_checks = false;
         bool generate_compact_dex = true;
-        bool use_jitzygote_image = false;
+        bool use_jitzygote = false;
         const char* compilation_reason = nullptr;
     };
 
@@ -175,6 +175,7 @@
         default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
+        default_expected_flags_["--boot-image"] = FLAG_UNUSED;
 
         // Arch
         default_expected_flags_["--instruction-set"] = "=arm64";
@@ -190,6 +191,7 @@
         default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED;
         default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED;
         default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED;
+        default_expected_flags_["--force-jit-zygote"] = FLAG_UNUSED;
 
         // Debug
         default_expected_flags_["--debuggable"] = FLAG_UNUSED;
@@ -256,7 +258,7 @@
                           args->target_sdk_version,
                           args->enable_hidden_api_checks,
                           args->generate_compact_dex,
-                          args->use_jitzygote_image,
+                          args->use_jitzygote,
                           args->compilation_reason);
         runner.Exec(/*exit_code=*/ 0);
     }
@@ -557,5 +559,22 @@
     VerifyExpectedFlags();
 }
 
+TEST_F(RunDex2OatTest, UseJitZygoteImage) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->use_jitzygote = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--force-jit-zygote", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, BootImage) {
+    setSystemProperty("dalvik.vm.boot-image", "foo.art:bar.art");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--boot-image", "=foo.art:bar.art");
+    VerifyExpectedFlags();
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 7082017..51f7716 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -13,7 +13,10 @@
     test_suites: ["device-tests"],
     clang: true,
     srcs: ["installd_utils_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "libutils",
@@ -33,7 +36,10 @@
     test_suites: ["device-tests"],
     clang: true,
     srcs: ["installd_cache_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -48,6 +54,7 @@
         "libasync_safe",
         "libdiskusage",
         "libinstalld",
+        "libziparchive",
         "liblog",
         "liblogwrap",
     ],
@@ -74,7 +81,10 @@
     test_suites: ["device-tests"],
     clang: true,
     srcs: ["installd_service_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -83,12 +93,14 @@
         "libprocessgroup",
         "libselinux",
         "libutils",
+        "packagemanager_aidl-cpp",
         "server_configurable_flags",
     ],
     static_libs: [
         "libasync_safe",
         "libdiskusage",
         "libinstalld",
+        "libziparchive",
         "liblog",
         "liblogwrap",
     ],
@@ -115,7 +127,10 @@
     test_suites: ["device-tests"],
     clang: true,
     srcs: ["installd_dexopt_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -158,7 +173,10 @@
     test_suites: ["device-tests"],
     clang: true,
     srcs: ["installd_otapreopt_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "libcutils",
@@ -167,6 +185,26 @@
     ],
     static_libs: [
         "liblog",
-        "libotapreoptparameters"
+        "libotapreoptparameters",
+    ],
+}
+
+cc_test {
+    name: "installd_file_test",
+    test_suites: ["device-tests"],
+    clang: true,
+    srcs: ["installd_file_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libutils",
+    ],
+    static_libs: [
+        "libinstalld",
+        "liblog",
     ],
 }
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 863cdfe..72f5f4b 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -122,6 +122,7 @@
 
         service = new InstalldNativeService();
         testUuid = kTestUuid;
+        system("rm -rf /data/local/tmp/user");
         system("mkdir -p /data/local/tmp/user/0");
     }
 
diff --git a/cmds/installd/tests/installd_file_test.cpp b/cmds/installd/tests/installd_file_test.cpp
new file mode 100644
index 0000000..00fb308
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restorable_file.h"
+#include "unique_file.h"
+#include "utils.h"
+
+#undef LOG_TAG
+#define LOG_TAG "installd_file_test"
+
+namespace {
+
+constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data";
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+void UnlinkWithAssert(const std::string& path) {
+    ASSERT_EQ(0, unlink(path.c_str()));
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+// Add these as macros as functions make it hard to tell where the failure has happened.
+#define ASSERT_FILE_NOT_EXISTING(path)           \
+    {                                            \
+        struct stat st;                          \
+        ASSERT_NE(0, ::stat(path.c_str(), &st)); \
+    }
+#define ASSERT_FILE_EXISTING(path)               \
+    {                                            \
+        struct stat st;                          \
+        ASSERT_EQ(0, ::stat(path.c_str(), &st)); \
+    }
+#define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path))
+#define ASSERT_FILE_OPEN(path, fd)       \
+    {                                    \
+        fd = open(path.c_str(), O_RDWR); \
+        ASSERT_TRUE(fd >= 0);            \
+    }
+#define ASSERT_WRITE_TO_FD(fd, content) \
+    ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd)))
+
+class FileTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777));
+    }
+
+    virtual void TearDown() {
+        system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str());
+    }
+
+    std::string GetTestFilePath(const std::string& fileName) {
+        return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str());
+    }
+
+    void CreateTestFileWithContents(const std::string& path, const std::string& content) {
+        ALOGI("CreateTestFileWithContents:%s", path.c_str());
+        ASSERT_TRUE(android::base::WriteStringToFile(content, path));
+    }
+
+    std::string GetTestName() {
+        std::string name(testing::UnitTest::GetInstance()->current_test_info()->name());
+        return name;
+    }
+
+    std::string ReadTestFile(const std::string& path) {
+        std::string content;
+        bool r = android::base::ReadFileToString(path, &content);
+        if (!r) {
+            PLOG(ERROR) << "Cannot read file:" << path;
+        }
+        return content;
+    }
+};
+
+TEST_F(FileTest, TestUniqueFileMoveConstruction) {
+    const int fd = 101;
+    std::string testFile = GetTestFilePath(GetTestName());
+    UniqueFile uf1(fd, testFile);
+    uf1.DisableAutoClose();
+
+    UniqueFile uf2(std::move(uf1));
+
+    ASSERT_EQ(fd, uf2.fd());
+    ASSERT_EQ(testFile, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileAssignment) {
+    const int fd1 = 101;
+    const int fd2 = 102;
+    std::string testFile1 = GetTestFilePath(GetTestName());
+    std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+    UniqueFile uf1(fd1, testFile1);
+    uf1.DisableAutoClose();
+
+    UniqueFile uf2(fd2, testFile2);
+    uf2.DisableAutoClose();
+
+    ASSERT_EQ(fd2, uf2.fd());
+    ASSERT_EQ(testFile2, uf2.path());
+
+    uf2 = std::move(uf1);
+
+    ASSERT_EQ(fd1, uf2.fd());
+    ASSERT_EQ(testFile1, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileCleanup) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    int fd;
+    ASSERT_FILE_OPEN(testFile, fd);
+
+    { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); }
+
+    ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestUniqueFileNoCleanup) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    int fd;
+    ASSERT_FILE_OPEN(testFile, fd);
+
+    {
+        UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert);
+        uf.DisableCleanup();
+    }
+
+    ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestUniqueFileFd) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    int fd;
+    ASSERT_FILE_OPEN(testFile, fd);
+
+    UniqueFile uf(fd, testFile, UnlinkWithAssert);
+
+    ASSERT_EQ(fd, uf.fd());
+
+    uf.reset();
+
+    ASSERT_EQ(-1, uf.fd());
+}
+
+TEST_F(FileTest, TestRestorableFileMoveConstruction) {
+    std::string testFile = GetTestFilePath(GetTestName());
+
+    RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600);
+    int fd = rf1.fd();
+
+    RestorableFile rf2(std::move(rf1));
+
+    ASSERT_EQ(fd, rf2.fd());
+    ASSERT_EQ(testFile, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileAssignment) {
+    std::string testFile1 = GetTestFilePath(GetTestName());
+    std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+    RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600);
+    int fd1 = rf1.fd();
+
+    RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600);
+    int fd2 = rf2.fd();
+
+    ASSERT_EQ(fd2, rf2.fd());
+    ASSERT_EQ(testFile2, rf2.path());
+
+    rf2 = std::move(rf1);
+
+    ASSERT_EQ(fd1, rf2.fd());
+    ASSERT_EQ(testFile1, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+        ASSERT_FILE_EXISTING(tmpFile);
+
+        const UniqueFile& uf = rf.GetUniqueFile();
+
+        ASSERT_EQ(rf.fd(), uf.fd());
+        ASSERT_EQ(rf.path(), uf.path());
+
+        rf.reset();
+
+        ASSERT_EQ(rf.fd(), uf.fd());
+        ASSERT_EQ(rf.path(), uf.path());
+        ASSERT_EQ(-1, rf.fd());
+        ASSERT_TRUE(rf.path().empty());
+    }
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+        ASSERT_FILE_EXISTING(tmpFile);
+
+        const UniqueFile& uf = rf.GetUniqueFile();
+
+        ASSERT_EQ(rf.fd(), uf.fd());
+        ASSERT_EQ(rf.path(), uf.path());
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(backupFile);
+
+        rf.CommitWorkFile();
+
+        ASSERT_EQ(rf.fd(), uf.fd());
+        ASSERT_EQ(rf.path(), uf.path());
+        ASSERT_EQ(-1, rf.fd());
+        ASSERT_EQ(testFile, rf.path());
+    }
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+        ASSERT_FILE_EXISTING(tmpFile);
+        ASSERT_FILE_NOT_EXISTING(testFile);
+
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+        ASSERT_FILE_EXISTING(testFile);
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+    CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+        ASSERT_FILE_EXISTING(testFile);
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileCommitted) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+        ASSERT_FILE_EXISTING(tmpFile);
+        ASSERT_FILE_NOT_EXISTING(testFile);
+
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(backupFile);
+
+        ASSERT_TRUE(rf.CommitWorkFile());
+        rf.RemoveBackupFile();
+
+        ASSERT_FILE_CONTENT(testFile, "NewContent");
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+    ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_EXISTING(backupFile);
+
+        ASSERT_TRUE(rf.CommitWorkFile());
+
+        ASSERT_FILE_EXISTING(backupFile);
+        ASSERT_FILE_CONTENT(testFile, "NewContent");
+
+        rf.RemoveBackupFile();
+
+        ASSERT_FILE_NOT_EXISTING(backupFile);
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+    CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+        ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+        ASSERT_TRUE(rf.CommitWorkFile());
+
+        ASSERT_FILE_CONTENT(testFile, "NewContent");
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_NOT_EXISTING(backupFile);
+
+        // Now remove tmp file to force commit failure.
+        close(rf.fd());
+        ASSERT_EQ(0, unlink(tmpFile.c_str()));
+        ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+        ASSERT_FALSE(rf.CommitWorkFile());
+
+        ASSERT_EQ(-1, rf.fd());
+        ASSERT_EQ(testFile, rf.path());
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+        ASSERT_TRUE(rf.RestoreBackupFile());
+    }
+
+    ASSERT_FILE_NOT_EXISTING(testFile);
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_EXISTING(backupFile);
+
+        // Now remove tmp file to force commit failure.
+        close(rf.fd());
+        ASSERT_EQ(0, unlink(tmpFile.c_str()));
+        ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+        ASSERT_FALSE(rf.CommitWorkFile());
+
+        ASSERT_EQ(-1, rf.fd());
+        ASSERT_EQ(testFile, rf.path());
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_NOT_EXISTING(tmpFile);
+        ASSERT_FILE_EXISTING(backupFile);
+
+        ASSERT_TRUE(rf.RestoreBackupFile());
+    }
+
+    ASSERT_FILE_EXISTING(testFile);
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    {
+        RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+        ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+        ASSERT_TRUE(rf.CreateBackupFile());
+
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_EXISTING(backupFile);
+
+        rf.ResetAndRemoveAllFiles();
+
+        ASSERT_EQ(-1, rf.fd());
+        ASSERT_FILE_NOT_EXISTING(tmpFile);
+        ASSERT_FILE_NOT_EXISTING(testFile);
+        ASSERT_FILE_NOT_EXISTING(backupFile);
+    }
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(testFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+
+    RestorableFile::RemoveAllFiles(testFile);
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(testFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) {
+    std::string testFile = GetTestFilePath(GetTestName());
+    std::string tmpFile = testFile + kTmpFileSuffix;
+    std::string backupFile = testFile + kBackupFileSuffix;
+    CreateTestFileWithContents(testFile, "OriginalContent");
+    CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent");
+
+    RestorableFile::RemoveAllFiles(testFile);
+
+    ASSERT_FILE_NOT_EXISTING(tmpFile);
+    ASSERT_FILE_NOT_EXISTING(testFile);
+    ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_file_test.xml b/cmds/installd/tests/installd_file_test.xml
new file mode 100644
index 0000000..5ec6e3f
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+           root support. -->
+<configuration description="Runs installd_file_test.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push"
+                value="installd_file_test->/data/local/tmp/installd_file_test" />
+    </target_preparer>
+
+    <!-- The test requires root for file access (rollback. -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="installd_file_test" />
+    </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 1e7559d..b831515 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -18,10 +18,11 @@
 #include <string>
 
 #include <fcntl.h>
+#include <pwd.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/statvfs.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/xattr.h>
 
 #include <android-base/file.h>
@@ -32,8 +33,10 @@
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
 
-#include "binder_test_utils.h"
+#include <android/content/pm/IPackageManagerNative.h>
+#include <binder/IServiceManager.h>
 #include "InstalldNativeService.h"
+#include "binder_test_utils.h"
 #include "dexopt.h"
 #include "globals.h"
 #include "utils.h"
@@ -41,6 +44,34 @@
 using android::base::StringPrintf;
 
 namespace android {
+std::string get_package_name(uid_t uid) {
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<content::pm::IPackageManagerNative> package_mgr;
+    if (sm.get() == nullptr) {
+        LOG(INFO) << "Cannot find service manager";
+    } else {
+        sp<IBinder> binder = sm->getService(String16("package_native"));
+        if (binder.get() == nullptr) {
+            LOG(INFO) << "Cannot find package_native";
+        } else {
+            package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
+        }
+    }
+    // find package name
+    std::string pkg;
+    if (package_mgr != nullptr) {
+        std::vector<std::string> names;
+        binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names);
+        if (!status.isOk()) {
+            LOG(INFO) << "getNamesForUids failed: %s", status.exceptionMessage().c_str();
+        } else {
+            if (!names[0].empty()) {
+                pkg = names[0].c_str();
+            }
+        }
+    }
+    return pkg;
+}
 namespace installd {
 
 constexpr const char* kTestUuid = "TEST";
@@ -107,6 +138,7 @@
 
         service = new InstalldNativeService();
         testUuid = kTestUuid;
+        system("rm -rf /data/local/tmp/user");
         system("mkdir -p /data/local/tmp/user/0");
 
         init_globals_from_data_and_root();
@@ -247,7 +279,50 @@
     EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa"));
     EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf));
 }
+TEST_F(ServiceTest, GetAppSize) {
+    struct stat s;
 
+    std::string externalPicDir =
+            StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str());
+    if (stat(externalPicDir.c_str(), &s) == 0) {
+        // fetch the appId from the uid of the external storage owning app
+        int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid);
+        // Fetch Package Name for the external storage owning app uid
+        std::string pkg = get_package_name(s.st_uid);
+
+        std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingExternalFile;
+        std::vector<int64_t> ceDataInodes;
+
+        std::vector<std::string> codePaths;
+        std::vector<std::string> packageNames;
+        // set up parameters
+        packageNames.push_back(pkg);
+        ceDataInodes.push_back(0);
+        // initialise the mounts
+        service->invalidateMounts();
+        // call the getAppSize to get the current size of the external storage owning app
+        service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA,
+                            externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize);
+        // add a file with 20MB size to the external storage
+        std::string externalFileLocation =
+                StringPrintf("%s/Pictures/%s", getenv("EXTERNAL_STORAGE"), "External.jpg");
+        std::string externalFileContentCommand =
+                StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", externalFileLocation.c_str());
+        system(externalFileContentCommand.c_str());
+        // call the getAppSize again to get the new size of the external storage owning app
+        service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA,
+                            externalStorageAppId, ceDataInodes, codePaths,
+                            &externalStorageSizeAfterAddingExternalFile);
+        // check that the size before adding the file and after should be the same, as the app size
+        // is not changed.
+        for (size_t i = 0; i < externalStorageSize.size(); i++) {
+            ASSERT_TRUE(externalStorageSize[i] == externalStorageSizeAfterAddingExternalFile[i]);
+        }
+        // remove the external file
+        std::string removeCommand = StringPrintf("rm -f %s", externalFileLocation.c_str());
+        system(removeCommand.c_str());
+    }
+}
 static bool mkdirs(const std::string& path, mode_t mode) {
     struct stat sb;
     if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 80c0548..32922ca 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,14 @@
 }
 
 cc_binary {
+    name: "servicemanager.microdroid",
+    defaults: ["servicemanager_defaults"],
+    init_rc: ["servicemanager.microdroid.rc"],
+    srcs: ["main.cpp"],
+    bootstrap: true,
+}
+
+cc_binary {
     name: "servicemanager.recovery",
     stem: "servicemanager",
     recovery: true,
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4e44ac7..555be1e 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -28,6 +28,9 @@
 
 #ifndef VENDORSERVICEMANAGER
 #include <vintf/VintfObject.h>
+#ifdef __ANDROID_RECOVERY__
+#include <vintf/VintfObjectRecovery.h>
+#endif // __ANDROID_RECOVERY__
 #include <vintf/constants.h>
 #endif  // !VENDORSERVICEMANAGER
 
@@ -37,16 +40,33 @@
 namespace android {
 
 #ifndef VENDORSERVICEMANAGER
+
 struct ManifestWithDescription {
     std::shared_ptr<const vintf::HalManifest> manifest;
     const char* description;
 };
+static std::vector<ManifestWithDescription> GetManifestsWithDescription() {
+#ifdef __ANDROID_RECOVERY__
+    auto vintfObject = vintf::VintfObjectRecovery::GetInstance();
+    if (vintfObject == nullptr) {
+        LOG(ERROR) << "NULL VintfObjectRecovery!";
+        return {};
+    }
+    return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}};
+#else
+    auto vintfObject = vintf::VintfObject::GetInstance();
+    if (vintfObject == nullptr) {
+        LOG(ERROR) << "NULL VintfObject!";
+        return {};
+    }
+    return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"},
+            ManifestWithDescription{vintfObject->getFrameworkHalManifest(), "framework"}};
+#endif
+}
+
 // func true -> stop search and forEachManifest will return true
 static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
-    for (const ManifestWithDescription& mwd : {
-            ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
-            ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
-        }) {
+    for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) {
         if (mwd.manifest == nullptr) {
           LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
           // note, we explicitly do not retry here, so that we can detect VINTF
@@ -93,8 +113,8 @@
     if (!found) {
         // Although it is tested, explicitly rebuilding qualified name, in case it
         // becomes something unexpected.
-        LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
-                   << aname.instance << " in the VINTF manifest.";
+        LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
+                  << aname.instance << " in the VINTF manifest.";
     }
 
     return found;
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
new file mode 100644
index 0000000..e01f132
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.microdroid.rc
@@ -0,0 +1,8 @@
+service servicemanager /system/bin/servicemanager.microdroid
+    class core
+    user system
+    group system readproc
+    critical
+    onrestart restart apexd
+    task_profiles ServiceCapacityLow
+    shutdown critical
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 0dd29e0..e5d689f 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -6,8 +6,8 @@
     onrestart restart apexd
     onrestart restart audioserver
     onrestart restart gatekeeperd
-    onrestart class_restart main
-    onrestart class_restart hal
-    onrestart class_restart early_hal
+    onrestart class_restart --only-enabled main
+    onrestart class_restart --only-enabled hal
+    onrestart class_restart --only-enabled early_hal
     task_profiles ServiceCapacityLow
     shutdown critical
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 931c5e3..31dee23 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -191,6 +191,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.wifi.direct.prebuilt.xml",
+    src: "android.hardware.wifi.direct.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.wifi.passpoint.prebuilt.xml",
     src: "android.hardware.wifi.passpoint.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -239,6 +245,12 @@
 }
 
 prebuilt_etc {
+    name: "go_handheld_core_hardware.prebuilt.xml",
+    src: "go_handheld_core_hardware.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "handheld_core_hardware.prebuilt.xml",
     src: "handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.software.opengles.deqp.level-2022-03-01.xml b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..0a11835
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+     dEQP tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+    <feature name="android.software.opengles.deqp.level" version="132514561" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..8deebc0
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+     tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+    <feature name="android.software.vulkan.deqp.level" version="132514561" />
+</permissions>
diff --git a/include/android/input.h b/include/android/input.h
index bb98beb..7642215 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -947,9 +947,10 @@
  * and {@link AMotionEvent_fromJava()}.
  * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
  * The underlying Java object remains valid and does not change its state.
+ *
+ * Available since API level 31.
  */
-
-void AInputEvent_release(const AInputEvent* event);
+void AInputEvent_release(const AInputEvent* event) __INTRODUCED_IN(31);
 
 /*** Accessors for key events only. ***/
 
@@ -1001,8 +1002,10 @@
  * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
  * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
  * returned by this function must be disposed using {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
  */
-const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) __INTRODUCED_IN(31);
 
 /*** Accessors for motion events only. ***/
 
@@ -1324,8 +1327,10 @@
  * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
  * AInputEvent_* functions. The object returned by this function must be disposed using
  * {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
  */
-const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31);
 
 struct AInputQueue;
 /**
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 509ee0e..4c83a14 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -216,6 +216,61 @@
  */
 void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29);
 
+/*
+ * Set the socket tag and owning UID for traffic statistics on the specified
+ * socket.
+ *
+ * Subsequent calls always replace any existing parameters. The socket tag and
+ * uid (if set) are kept when the socket is sent to another process using binder
+ * IPCs or other mechanisms such as UNIX socket fd passing. Any app can accept
+ * blame for future traffic performed on a socket originally created by another
+ * app by calling this method with its own UID (or calling
+ * android_tag_socket(int sockfd, int tag)). However, only apps holding the
+ * android.Manifest.permission#UPDATE_DEVICE_STATS permission may assign blame
+ * to another UIDs. If unset (default) the socket tag is 0, and the uid is the
+ * socket creator's uid.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33);
+
+/*
+ * Set the socket tag for traffic statistics on the specified socket.
+ *
+ * This function tags the socket with the caller's UID (accepting blame for
+ * future traffic performed on this socket) even if the socket was originally
+ * opened by another UID or was previously tagged by another UID. Subsequent
+ * calls always replace any existing parameters. The socket tag is kept when the
+ * socket is sent to another process using binder IPCs or other mechanisms such
+ * as UNIX socket fd passing.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33);
+
+/*
+ * Untag a network socket.
+ *
+ * Future traffic on this socket will no longer be associated with any
+ * previously configured tag and uid. If the socket was created by another UID
+ * or was previously tagged by another UID, calling this function will clear the
+ * statistics parameters, and thus the UID blamed for traffic on the socket will
+ * be the UID that originally created the socket, even if the socket was
+ * subsequently tagged by a different UID.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_untag_socket(int sockfd) __INTRODUCED_IN(33);
+
 __END_DECLS
 
 #endif  // ANDROID_MULTINETWORK_H
diff --git a/include/input/Input.h b/include/input/Input.h
index 2e326cb..dce6ccb 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1015,6 +1015,25 @@
     std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
 };
 
+/*
+ * Describes a unique request to enable or disable Pointer Capture.
+ */
+struct PointerCaptureRequest {
+public:
+    inline PointerCaptureRequest() : enable(false), seq(0) {}
+    inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+    inline bool operator==(const PointerCaptureRequest& other) const {
+        return enable == other.enable && seq == other.seq;
+    }
+    explicit inline operator bool() const { return enable; }
+
+    // True iff this is a request to enable Pointer Capture.
+    bool enable;
+
+    // The sequence number for the request.
+    uint32_t seq;
+};
+
 } // namespace android
 
 #endif // _LIBINPUT_INPUT_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d8101fa..7448308 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -202,7 +202,6 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
-    min_sdk_version: "30",
 
     tidy: true,
     tidy_flags: [
@@ -225,10 +224,7 @@
         "portability*",
     ],
 
-    pgo: {
-        sampling: true,
-        profile_file: "libbinder/libbinder.profdata",
-    },
+    afdo: true,
 }
 
 cc_defaults {
@@ -333,7 +329,6 @@
 cc_library {
     name: "libbinder_rpc_unstable",
     srcs: ["libbinder_rpc_unstable.cpp"],
-    defaults: ["libbinder_ndk_host_user"],
     shared_libs: [
         "libbase",
         "libbinder",
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9e04ffe..13f0a4c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -352,11 +352,6 @@
     return gDisableBackgroundScheduling.load(std::memory_order_relaxed);
 }
 
-sp<ProcessState> IPCThreadState::process()
-{
-    return mProcess;
-}
-
 status_t IPCThreadState::clearLastError()
 {
     const status_t err = mLastError;
@@ -1410,23 +1405,6 @@
         }
 }
 
-status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
-{
-    int ret = 0;
-    binder_frozen_status_info info;
-    info.pid = pid;
-
-#if defined(__ANDROID__)
-    if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
-        ret = -errno;
-#endif
-    *sync_received = info.sync_recv;
-    *async_received = info.async_recv;
-
-    return ret;
-}
-
-#ifndef __ANDROID_VNDK__
 status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
                                               uint32_t *async_received)
 {
@@ -1443,7 +1421,6 @@
 
     return ret;
 }
-#endif
 
 status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
     struct binder_freeze_info info;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7027a4b..f84f639 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -739,6 +739,17 @@
     }
 }
 
+binder::Status Parcel::enforceNoDataAvail() const {
+    const auto n = dataAvail();
+    if (n == 0) {
+        return binder::Status::ok();
+    }
+    return binder::Status::
+            fromExceptionCode(binder::Status::Exception::EX_BAD_PARCELABLE,
+                              String8::format("Parcel data not fully consumed, unread size: %zu",
+                                              n));
+}
+
 size_t Parcel::objectsCount() const
 {
     return mObjectsSize;
@@ -2202,12 +2213,14 @@
               type == BINDER_TYPE_FD)) {
             // We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support
             // them in libbinder. If we do receive them, it probably means a kernel bug; try to
-            // recover gracefully by clearing out the objects, and releasing the objects we do
-            // know about.
+            // recover gracefully by clearing out the objects.
             android_errorWriteLog(0x534e4554, "135930648");
+            android_errorWriteLog(0x534e4554, "203847542");
             ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n",
                   __func__, type, (uint64_t)offset);
-            releaseObjects();
+
+            // WARNING: callers of ipcSetDataReference need to make sure they
+            // don't rely on mObjectsSize in their release_func.
             mObjectsSize = 0;
             break;
         }
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4f21cda..269b086 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -176,6 +176,11 @@
 {
     AutoMutex _l(mLock);
     if (!mThreadPoolStarted) {
+        if (mMaxThreads == 0) {
+            ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
+                  "*startThreadPool when zero threads are requested.");
+        }
+
         mThreadPoolStarted = true;
         spawnPooledThread(true);
     }
@@ -315,11 +320,6 @@
                 //
                 // Note that this is not race-free if the context manager
                 // dies while this code runs.
-                //
-                // TODO: add a driver API to wait for context manager, or
-                // stop special casing handle 0 for context manager and add
-                // a driver API to get a handle to the context manager with
-                // proper reference counting.
 
                 IPCThreadState* ipc = IPCThreadState::self();
 
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 93ed50e..ace5cd5 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -287,8 +287,8 @@
 
     RpcConnectionHeader header;
     if (status == OK) {
-        status = client->interruptableReadFully(server->mShutdownTrigger.get(), &header,
-                                                sizeof(header), {});
+        iovec iov{&header, sizeof(header)};
+        status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
         if (status != OK) {
             ALOGE("Failed to read ID for client connecting to RPC server: %s",
                   statusToString(status).c_str());
@@ -301,8 +301,9 @@
         if (header.sessionIdSize > 0) {
             if (header.sessionIdSize == kSessionIdBytes) {
                 sessionId.resize(header.sessionIdSize);
-                status = client->interruptableReadFully(server->mShutdownTrigger.get(),
-                                                        sessionId.data(), sessionId.size(), {});
+                iovec iov{sessionId.data(), sessionId.size()};
+                status =
+                        client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
                 if (status != OK) {
                     ALOGE("Failed to read session ID for client connecting to RPC server: %s",
                           statusToString(status).c_str());
@@ -331,8 +332,8 @@
                     .version = protocolVersion,
             };
 
-            status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &response,
-                                                     sizeof(response), {});
+            iovec iov{&response, sizeof(response)};
+            status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, {});
             if (status != OK) {
                 ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
                 // still need to cleanup before we can return
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index a5a2bb1..b84395e 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -615,8 +615,9 @@
         header.options |= RPC_CONNECTION_OPTION_INCOMING;
     }
 
+    iovec headerIov{&header, sizeof(header)};
     auto sendHeaderStatus =
-            server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header), {});
+            server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {});
     if (sendHeaderStatus != OK) {
         ALOGE("Could not write connection header to socket: %s",
               statusToString(sendHeaderStatus).c_str());
@@ -624,9 +625,10 @@
     }
 
     if (sessionId.size() > 0) {
+        iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())),
+                         sessionId.size()};
         auto sendSessionIdStatus =
-                server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
-                                                sessionId.size(), {});
+                server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {});
         if (sendSessionIdStatus != OK) {
             ALOGE("Could not write session ID ('%s') to socket: %s",
                   base::HexString(sessionId.data(), sessionId.size()).c_str(),
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 09b3d68..6286c9c 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -19,6 +19,7 @@
 #include "RpcState.h"
 
 #include <android-base/hex.h>
+#include <android-base/macros.h>
 #include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
@@ -309,22 +310,18 @@
 }
 
 status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
-                           const sp<RpcSession>& session, const char* what, const void* data,
-                           size_t size, const std::function<status_t()>& altPoll) {
-    LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
-                   android::base::HexString(data, size).c_str());
-
-    if (size > std::numeric_limits<ssize_t>::max()) {
-        ALOGE("Cannot send %s at size %zu (too big)", what, size);
-        (void)session->shutdownAndWait(false);
-        return BAD_VALUE;
+                           const sp<RpcSession>& session, const char* what, iovec* iovs,
+                           size_t niovs, const std::function<status_t()>& altPoll) {
+    for (size_t i = 0; i < niovs; i++) {
+        LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+                       android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
     }
 
     if (status_t status =
                 connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
-                                                                  data, size, altPoll);
+                                                                  iovs, niovs, altPoll);
         status != OK) {
-        LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+        LOG_RPC_DETAIL("Failed to write %s (%zu iovs) on RpcTransport %p, error: %s", what, niovs,
                        connection->rpcTransport.get(), statusToString(status).c_str());
         (void)session->shutdownAndWait(false);
         return status;
@@ -334,34 +331,30 @@
 }
 
 status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
-                          const sp<RpcSession>& session, const char* what, void* data,
-                          size_t size) {
-    if (size > std::numeric_limits<ssize_t>::max()) {
-        ALOGE("Cannot rec %s at size %zu (too big)", what, size);
-        (void)session->shutdownAndWait(false);
-        return BAD_VALUE;
-    }
-
+                          const sp<RpcSession>& session, const char* what, iovec* iovs,
+                          size_t niovs) {
     if (status_t status =
                 connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
-                                                                 data, size, {});
+                                                                 iovs, niovs, {});
         status != OK) {
-        LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+        LOG_RPC_DETAIL("Failed to read %s (%zu iovs) on RpcTransport %p, error: %s", what, niovs,
                        connection->rpcTransport.get(), statusToString(status).c_str());
         (void)session->shutdownAndWait(false);
         return status;
     }
 
-    LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
-                   android::base::HexString(data, size).c_str());
+    for (size_t i = 0; i < niovs; i++) {
+        LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+                       android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
+    }
     return OK;
 }
 
 status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                           const sp<RpcSession>& session, uint32_t* version) {
     RpcNewSessionResponse response;
-    if (status_t status =
-                rpcRec(connection, session, "new session response", &response, sizeof(response));
+    iovec iov{&response, sizeof(response)};
+    if (status_t status = rpcRec(connection, session, "new session response", &iov, 1);
         status != OK) {
         return status;
     }
@@ -374,14 +367,15 @@
     RpcOutgoingConnectionInit init{
             .msg = RPC_CONNECTION_INIT_OKAY,
     };
-    return rpcSend(connection, session, "connection init", &init, sizeof(init));
+    iovec iov{&init, sizeof(init)};
+    return rpcSend(connection, session, "connection init", &iov, 1);
 }
 
 status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                       const sp<RpcSession>& session) {
     RpcOutgoingConnectionInit init;
-    if (status_t status = rpcRec(connection, session, "connection init", &init, sizeof(init));
-        status != OK)
+    iovec iov{&init, sizeof(init)};
+    if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK)
         return status;
 
     static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY));
@@ -514,17 +508,6 @@
             .flags = flags,
             .asyncNumber = asyncNumber,
     };
-    CommandData transactionData(sizeof(RpcWireHeader) + sizeof(RpcWireTransaction) +
-                                data.dataSize());
-    if (!transactionData.valid()) {
-        return NO_MEMORY;
-    }
-
-    memcpy(transactionData.data() + 0, &command, sizeof(RpcWireHeader));
-    memcpy(transactionData.data() + sizeof(RpcWireHeader), &transaction,
-           sizeof(RpcWireTransaction));
-    memcpy(transactionData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireTransaction), data.data(),
-           data.dataSize());
 
     constexpr size_t kWaitMaxUs = 1000000;
     constexpr size_t kWaitLogUs = 10000;
@@ -550,8 +533,13 @@
         return drainCommands(connection, session, CommandType::CONTROL_ONLY);
     };
 
-    if (status_t status = rpcSend(connection, session, "transaction", transactionData.data(),
-                                  transactionData.size(), drainRefs);
+    iovec iovs[]{
+            {&command, sizeof(RpcWireHeader)},
+            {&transaction, sizeof(RpcWireTransaction)},
+            {const_cast<uint8_t*>(data.data()), data.dataSize()},
+    };
+    if (status_t status =
+                rpcSend(connection, session, "transaction", iovs, arraysize(iovs), drainRefs);
         status != OK) {
         // TODO(b/167966510): need to undo onBinderLeaving - we know the
         // refcount isn't successfully transferred.
@@ -584,8 +572,8 @@
                                 const sp<RpcSession>& session, Parcel* reply) {
     RpcWireHeader command;
     while (true) {
-        if (status_t status = rpcRec(connection, session, "command header (for reply)", &command,
-                                     sizeof(command));
+        iovec iov{&command, sizeof(command)};
+        if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1);
             status != OK)
             return status;
 
@@ -599,8 +587,8 @@
     CommandData data(command.bodySize);
     if (!data.valid()) return NO_MEMORY;
 
-    if (status_t status = rpcRec(connection, session, "reply body", data.data(), command.bodySize);
-        status != OK)
+    iovec iov{data.data(), command.bodySize};
+    if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK)
         return status;
 
     if (command.bodySize < sizeof(RpcWireReply)) {
@@ -653,11 +641,8 @@
             .command = RPC_COMMAND_DEC_STRONG,
             .bodySize = sizeof(RpcDecStrong),
     };
-    if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd));
-        status != OK)
-        return status;
-
-    return rpcSend(connection, session, "dec ref body", &body, sizeof(body));
+    iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
+    return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs));
 }
 
 status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
@@ -665,8 +650,8 @@
     LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
 
     RpcWireHeader command;
-    if (status_t status = rpcRec(connection, session, "command header (for server)", &command,
-                                 sizeof(command));
+    iovec iov{&command, sizeof(command)};
+    if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1);
         status != OK)
         return status;
 
@@ -726,9 +711,8 @@
     if (!transactionData.valid()) {
         return NO_MEMORY;
     }
-    if (status_t status = rpcRec(connection, session, "transaction body", transactionData.data(),
-                                 transactionData.size());
-        status != OK)
+    iovec iov{transactionData.data(), transactionData.size()};
+    if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK)
         return status;
 
     return processTransactInternal(connection, session, std::move(transactionData));
@@ -965,16 +949,12 @@
             .status = replyStatus,
     };
 
-    CommandData replyData(sizeof(RpcWireHeader) + sizeof(RpcWireReply) + reply.dataSize());
-    if (!replyData.valid()) {
-        return NO_MEMORY;
-    }
-    memcpy(replyData.data() + 0, &cmdReply, sizeof(RpcWireHeader));
-    memcpy(replyData.data() + sizeof(RpcWireHeader), &rpcReply, sizeof(RpcWireReply));
-    memcpy(replyData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireReply), reply.data(),
-           reply.dataSize());
-
-    return rpcSend(connection, session, "reply", replyData.data(), replyData.size());
+    iovec iovs[]{
+            {&cmdReply, sizeof(RpcWireHeader)},
+            {&rpcReply, sizeof(RpcWireReply)},
+            {const_cast<uint8_t*>(reply.data()), reply.dataSize()},
+    };
+    return rpcSend(connection, session, "reply", iovs, arraysize(iovs));
 }
 
 status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection,
@@ -985,9 +965,8 @@
     if (!commandData.valid()) {
         return NO_MEMORY;
     }
-    if (status_t status =
-                rpcRec(connection, session, "dec ref body", commandData.data(), commandData.size());
-        status != OK)
+    iovec iov{commandData.data(), commandData.size()};
+    if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK)
         return status;
 
     if (command.bodySize != sizeof(RpcDecStrong)) {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index dba0a43..5cad394 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -24,6 +24,8 @@
 #include <optional>
 #include <queue>
 
+#include <sys/uio.h>
+
 namespace android {
 
 struct RpcWireHeader;
@@ -177,12 +179,12 @@
     };
 
     [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
-                                   const sp<RpcSession>& session, const char* what,
-                                   const void* data, size_t size,
+                                   const sp<RpcSession>& session, const char* what, iovec* iovs,
+                                   size_t niovs,
                                    const std::function<status_t()>& altPoll = nullptr);
     [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
-                                  const sp<RpcSession>& session, const char* what, void* data,
-                                  size_t size);
+                                  const sp<RpcSession>& session, const char* what, iovec* iovs,
+                                  size_t niovs);
 
     [[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
                                         const sp<RpcSession>& session, Parcel* reply);
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 7669518..2182e18 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -43,12 +43,10 @@
         return ret;
     }
 
-    template <typename Buffer, typename SendOrReceive>
-    status_t interruptableReadOrWrite(FdTrigger* fdTrigger, Buffer buffer, size_t size,
+    template <typename SendOrReceive>
+    status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                       SendOrReceive sendOrReceiveFun, const char* funName,
                                       int16_t event, const std::function<status_t()>& altPoll) {
-        const Buffer end = buffer + size;
-
         MAYBE_WAIT_IN_FLAKE_MODE;
 
         // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we
@@ -57,26 +55,61 @@
             return DEAD_OBJECT;
         }
 
+        // If iovs has one or more empty vectors at the end and
+        // we somehow advance past all the preceding vectors and
+        // pass some or all of the empty ones to sendmsg/recvmsg,
+        // the call will return processSize == 0. In that case
+        // we should be returning OK but instead return DEAD_OBJECT.
+        // To avoid this problem, we make sure here that the last
+        // vector at iovs[niovs - 1] has a non-zero length.
+        while (niovs > 0 && iovs[niovs - 1].iov_len == 0) {
+            niovs--;
+        }
+        if (niovs == 0) {
+            // The vectors are all empty, so we have nothing to send.
+            return OK;
+        }
+
         bool havePolled = false;
         while (true) {
-            ssize_t processSize = TEMP_FAILURE_RETRY(
-                    sendOrReceiveFun(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+            msghdr msg{
+                    .msg_iov = iovs,
+                    .msg_iovlen = niovs,
+            };
+            ssize_t processSize =
+                    TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL));
 
             if (processSize < 0) {
                 int savedErrno = errno;
 
                 // Still return the error on later passes, since it would expose
                 // a problem with polling
-                if (havePolled ||
-                    (!havePolled && savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
+                if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
                     LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno));
                     return -savedErrno;
                 }
             } else if (processSize == 0) {
                 return DEAD_OBJECT;
             } else {
-                buffer += processSize;
-                if (buffer == end) {
+                while (processSize > 0 && niovs > 0) {
+                    auto& iov = iovs[0];
+                    if (static_cast<size_t>(processSize) < iov.iov_len) {
+                        // Advance the base of the current iovec
+                        iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize;
+                        iov.iov_len -= processSize;
+                        break;
+                    }
+
+                    // The current iovec was fully written
+                    processSize -= iov.iov_len;
+                    iovs++;
+                    niovs--;
+                }
+                if (niovs == 0) {
+                    LOG_ALWAYS_FATAL_IF(processSize > 0,
+                                        "Reached the end of iovecs "
+                                        "with %zd bytes remaining",
+                                        processSize);
                     return OK;
                 }
             }
@@ -95,16 +128,16 @@
         }
     }
 
-    status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+    status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                      const std::function<status_t()>& altPoll) override {
-        return interruptableReadOrWrite(fdTrigger, reinterpret_cast<const uint8_t*>(data), size,
-                                        send, "send", POLLOUT, altPoll);
+        return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT,
+                                        altPoll);
     }
 
-    status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+    status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                     const std::function<status_t()>& altPoll) override {
-        return interruptableReadOrWrite(fdTrigger, reinterpret_cast<uint8_t*>(data), size, recv,
-                                        "recv", POLLIN, altPoll);
+        return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN,
+                                        altPoll);
     }
 
 private:
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 7f810b1..c05ea15 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -275,9 +275,9 @@
     RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
           : mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
     Result<size_t> peek(void* buf, size_t size) override;
-    status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+    status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                      const std::function<status_t()>& altPoll) override;
-    status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+    status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                     const std::function<status_t()>& altPoll) override;
 
 private:
@@ -303,68 +303,83 @@
     return ret;
 }
 
-status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
-                                                  size_t size,
+status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                                   const std::function<status_t()>& altPoll) {
-    auto buffer = reinterpret_cast<const uint8_t*>(data);
-    const uint8_t* end = buffer + size;
-
     MAYBE_WAIT_IN_FLAKE_MODE;
 
     // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
     // once. The trigger is also checked via triggerablePoll() after every SSL_write().
     if (fdTrigger->isTriggered()) return DEAD_OBJECT;
 
-    while (buffer < end) {
-        size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
-        auto [writeSize, errorQueue] = mSsl.call(SSL_write, buffer, todo);
-        if (writeSize > 0) {
-            buffer += writeSize;
-            errorQueue.clear();
+    size_t size = 0;
+    for (size_t i = 0; i < niovs; i++) {
+        const iovec& iov = iovs[i];
+        if (iov.iov_len == 0) {
             continue;
         }
-        // SSL_write() should never return 0 unless BIO_write were to return 0.
-        int sslError = mSsl.getError(writeSize);
-        // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
-        //   triggerablePoll()-ed. Then additionalEvent is no longer necessary.
-        status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
-                                                         "SSL_write", POLLIN, altPoll);
-        if (pollStatus != OK) return pollStatus;
-        // Do not advance buffer. Try SSL_write() again.
+        size += iov.iov_len;
+
+        auto buffer = reinterpret_cast<const uint8_t*>(iov.iov_base);
+        const uint8_t* end = buffer + iov.iov_len;
+        while (buffer < end) {
+            size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+            auto [writeSize, errorQueue] = mSsl.call(SSL_write, buffer, todo);
+            if (writeSize > 0) {
+                buffer += writeSize;
+                errorQueue.clear();
+                continue;
+            }
+            // SSL_write() should never return 0 unless BIO_write were to return 0.
+            int sslError = mSsl.getError(writeSize);
+            // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
+            //   triggerablePoll()-ed. Then additionalEvent is no longer necessary.
+            status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+                                                             "SSL_write", POLLIN, altPoll);
+            if (pollStatus != OK) return pollStatus;
+            // Do not advance buffer. Try SSL_write() again.
+        }
     }
     LOG_TLS_DETAIL("TLS: Sent %zu bytes!", size);
     return OK;
 }
 
-status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs,
                                                  const std::function<status_t()>& altPoll) {
-    auto buffer = reinterpret_cast<uint8_t*>(data);
-    uint8_t* end = buffer + size;
-
     MAYBE_WAIT_IN_FLAKE_MODE;
 
     // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
     // once. The trigger is also checked via triggerablePoll() after every SSL_write().
     if (fdTrigger->isTriggered()) return DEAD_OBJECT;
 
-    while (buffer < end) {
-        size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
-        auto [readSize, errorQueue] = mSsl.call(SSL_read, buffer, todo);
-        if (readSize > 0) {
-            buffer += readSize;
-            errorQueue.clear();
+    size_t size = 0;
+    for (size_t i = 0; i < niovs; i++) {
+        const iovec& iov = iovs[i];
+        if (iov.iov_len == 0) {
             continue;
         }
-        if (readSize == 0) {
-            // SSL_read() only returns 0 on EOF.
-            errorQueue.clear();
-            return DEAD_OBJECT;
+        size += iov.iov_len;
+
+        auto buffer = reinterpret_cast<uint8_t*>(iov.iov_base);
+        const uint8_t* end = buffer + iov.iov_len;
+        while (buffer < end) {
+            size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+            auto [readSize, errorQueue] = mSsl.call(SSL_read, buffer, todo);
+            if (readSize > 0) {
+                buffer += readSize;
+                errorQueue.clear();
+                continue;
+            }
+            if (readSize == 0) {
+                // SSL_read() only returns 0 on EOF.
+                errorQueue.clear();
+                return DEAD_OBJECT;
+            }
+            int sslError = mSsl.getError(readSize);
+            status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+                                                             "SSL_read", 0, altPoll);
+            if (pollStatus != OK) return pollStatus;
+            // Do not advance buffer. Try SSL_read() again.
         }
-        int sslError = mSsl.getError(readSize);
-        status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
-                                                         "SSL_read", 0, altPoll);
-        if (pollStatus != OK) return pollStatus;
-        // Do not advance buffer. Try SSL_read() again.
     }
     LOG_TLS_DETAIL("TLS: Received %zu bytes!", size);
     return OK;
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index bffab5e..949835b 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -27,7 +27,5 @@
   @utf8InCpp String diskImagePath;
   long versionCode;
   @utf8InCpp String versionName;
-  boolean hasBootClassPathJars;
-  boolean hasDex2OatBootClassPathJars;
-  boolean hasSystemServerClassPathJars;
+  boolean hasClassPathJars;
 }
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7d14315..f295417 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -93,20 +93,20 @@
 
 // ----------------------------------------------------------------------
 
-#define DECLARE_META_INTERFACE(INTERFACE)                               \
-public:                                                                 \
-    static const ::android::String16 descriptor;                        \
-    static ::android::sp<I##INTERFACE> asInterface(                     \
-            const ::android::sp<::android::IBinder>& obj);              \
-    virtual const ::android::String16& getInterfaceDescriptor() const;  \
-    I##INTERFACE();                                                     \
-    virtual ~I##INTERFACE();                                            \
-    static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl);     \
-    static const std::unique_ptr<I##INTERFACE>& getDefaultImpl();       \
-private:                                                                \
-    static std::unique_ptr<I##INTERFACE> default_impl;                  \
-public:                                                                 \
-
+#define DECLARE_META_INTERFACE(INTERFACE)                                                         \
+public:                                                                                           \
+    static const ::android::String16 descriptor;                                                  \
+    static ::android::sp<I##INTERFACE> asInterface(const ::android::sp<::android::IBinder>& obj); \
+    virtual const ::android::String16& getInterfaceDescriptor() const;                            \
+    I##INTERFACE();                                                                               \
+    virtual ~I##INTERFACE();                                                                      \
+    static bool setDefaultImpl(::android::sp<I##INTERFACE> impl);                                 \
+    static const ::android::sp<I##INTERFACE>& getDefaultImpl();                                   \
+                                                                                                  \
+private:                                                                                          \
+    static ::android::sp<I##INTERFACE> default_impl;                                              \
+                                                                                                  \
+public:
 
 #define __IINTF_CONCAT(x, y) (x ## y)
 
@@ -142,8 +142,8 @@
         }                                                                                          \
         return intr;                                                                               \
     }                                                                                              \
-    std::unique_ptr<ITYPE> ITYPE::default_impl;                                                    \
-    bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) {                                      \
+    ::android::sp<ITYPE> ITYPE::default_impl;                                                      \
+    bool ITYPE::setDefaultImpl(::android::sp<ITYPE> impl) {                                        \
         /* Only one user of this interface can use this function     */                            \
         /* at a time. This is a heuristic to detect if two different */                            \
         /* users in the same process use this function.              */                            \
@@ -154,7 +154,7 @@
         }                                                                                          \
         return false;                                                                              \
     }                                                                                              \
-    const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; }          \
+    const ::android::sp<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; }            \
     ITYPE::INAME() {}                                                                              \
     ITYPE::~INAME() {}
 
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 065e6e3..bf02099 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -51,17 +51,9 @@
     static  status_t            freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
 
     // Provide information about the state of a frozen process
-    static  status_t            getProcessFreezeInfo(pid_t pid, bool *sync_received,
-                                                    bool *async_received);
-
-    // TODO: Remove the above legacy duplicated function in next version
-#ifndef __ANDROID_VNDK__
     static  status_t            getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
                                                     uint32_t *async_received);
-#endif
 
-            sp<ProcessState>    process();
-            
             status_t            clearLastError();
 
             /**
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9670d7b..450e388 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <array>
 #include <map> // for legacy reasons
 #include <string>
 #include <type_traits>
@@ -53,6 +54,9 @@
 class RpcSession;
 class String8;
 class TextOutput;
+namespace binder {
+class Status;
+}
 
 class Parcel {
     friend class IPCThreadState;
@@ -130,6 +134,10 @@
                                          IPCThreadState* threadState = nullptr) const;
     bool                checkInterface(IBinder*) const;
 
+    // Verify there are no bytes left to be read on the Parcel.
+    // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
+    binder::Status enforceNoDataAvail() const;
+
     void                freeData();
 
     size_t              objectsCount() const;
@@ -224,6 +232,15 @@
         return writeData(val);
     }
 
+    template <typename T, size_t N>
+    status_t writeFixedArray(const std::array<T, N>& val) {
+        return writeData(val);
+    }
+    template <typename T, size_t N>
+    status_t writeFixedArray(const std::optional<std::array<T, N>>& val) {
+        return writeData(val);
+    }
+
     // Write an Enum vector with underlying type int8_t.
     // Does not use padding; each byte is contiguous.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -487,6 +504,15 @@
                             std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
+    template <typename T, size_t N>
+    status_t readFixedArray(std::array<T, N>* val) const {
+        return readData(val);
+    }
+    template <typename T, size_t N>
+    status_t readFixedArray(std::optional<std::array<T, N>>* val) const {
+        return readData(val);
+    }
+
     template<typename T>
     status_t            read(Flattenable<T>& val) const;
 
@@ -818,6 +844,16 @@
             || is_specialization_v<T, std::unique_ptr>
             || is_specialization_v<T, std::shared_ptr>;
 
+    // Tells if T is a fixed-size array.
+    template <typename T>
+    struct is_fixed_array : std::false_type {};
+
+    template <typename T, size_t N>
+    struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+    template <typename T>
+    static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
     // special int32 value to indicate NonNull or Null parcelables
     // This is fixed to be only 0 or 1 by contract, do not change.
     static constexpr int32_t kNonNullParcelableFlag = 1;
@@ -922,7 +958,9 @@
             if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
         } else if constexpr (std::is_base_of_v<Parcelable, T>) {
             if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag));
-        } else /* constexpr */ {  // could define this, but raise as error.
+        } else if constexpr (is_fixed_array_v<T>) {
+            if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
+        } else /* constexpr */ { // could define this, but raise as error.
             static_assert(dependent_false_v<CT>);
         }
         return writeData(*c);
@@ -961,6 +999,23 @@
         return OK;
     }
 
+    template <typename T, size_t N>
+    status_t writeData(const std::array<T, N>& val) {
+        static_assert(N <= std::numeric_limits<int32_t>::max());
+        status_t status = writeData(static_cast<int32_t>(N));
+        if (status != OK) return status;
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T));
+            return write(val.data(), val.size() * sizeof(T));
+        } else /* constexpr */ {
+            for (const auto& t : val) {
+                status = writeData(t);
+                if (status != OK) return status;
+            }
+            return OK;
+        }
+    }
+
     // readData function overloads.
     // Implementation detail: Function overloading improves code readability over
     // template overloading, but prevents readData<T> from being used for those types.
@@ -1053,9 +1108,8 @@
         int32_t peek;
         status_t status = readData(&peek);
         if (status != OK) return status;
-        if constexpr (is_specialization_v<T, std::vector>
-                || std::is_same_v<T, String16>
-                || std::is_same_v<T, std::string>) {
+        if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> ||
+                      std::is_same_v<T, String16> || std::is_same_v<T, std::string>) {
             if (peek == kNullVectorSize) {
                 c->reset();
                 return OK;
@@ -1065,12 +1119,15 @@
                 c->reset();
                 return OK;
             }
-        } else /* constexpr */ {  // could define this, but raise as error.
+        } else /* constexpr */ { // could define this, but raise as error.
             static_assert(dependent_false_v<CT>);
         }
         // create a new object.
         if constexpr (is_specialization_v<CT, std::optional>) {
-            c->emplace();
+            // Call default constructor explicitly
+            // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748
+            //   std::optional::emplace() doesn't work with nested types.
+            c->emplace(T());
         } else /* constexpr */ {
             T* const t = new (std::nothrow) T;  // contents read from Parcel below.
             if (t == nullptr) return NO_MEMORY;
@@ -1079,7 +1136,7 @@
         // rewind data ptr to reread (this is pretty quick), otherwise we could
         // pass an optional argument to readData to indicate a peeked value.
         setDataPosition(startPos);
-        if constexpr (is_specialization_v<T, std::vector>) {
+        if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) {
             return readData(&**c, READ_FLAG_SP_NULLABLE);  // nullable sp<> allowed now
         } else {
             return readData(&**c);
@@ -1142,6 +1199,41 @@
         return OK;
     }
 
+    template <typename T, size_t N>
+    status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const {
+        static_assert(N <= std::numeric_limits<int32_t>::max());
+        int32_t size;
+        status_t status = readInt32(&size);
+        if (status != OK) return status;
+        if (size < 0) return UNEXPECTED_NULL;
+        if (size != static_cast<int32_t>(N)) return BAD_VALUE;
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T)));
+            if (data == nullptr) return BAD_VALUE;
+            memcpy(val->data(), data, N * sizeof(T));
+        } else if constexpr (is_specialization_v<T, sp>) {
+            for (auto& t : *val) {
+                if (readFlags & READ_FLAG_SP_NULLABLE) {
+                    status = readNullableStrongBinder(&t); // allow nullable
+                } else {
+                    status = readStrongBinder(&t);
+                }
+                if (status != OK) return status;
+            }
+        } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays
+            for (auto& t : *val) {
+                status = readData(&t, readFlags);
+                if (status != OK) return status;
+            }
+        } else /* constexpr */ {
+            for (auto& t : *val) {
+                status = readData(&t);
+                if (status != OK) return status;
+            }
+        }
+        return OK;
+    }
+
     //-----------------------------------------------------------------------------
     private:
 
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index db8b5e9..348bfeb 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -28,6 +28,8 @@
 
 #include <binder/RpcCertificateFormat.h>
 
+#include <sys/uio.h>
+
 namespace android {
 
 class FdTrigger;
@@ -44,6 +46,9 @@
     /**
      * Read (or write), but allow to be interrupted by a trigger.
      *
+     * iovs - array of iovecs to perform the operation on. The elements
+     * of the array may be modified by this method.
+     *
      * altPoll - function to be called instead of polling, when needing to wait
      * to read/write data. If this returns an error, that error is returned from
      * this function.
@@ -53,10 +58,10 @@
      *   error - interrupted (failure or trigger)
      */
     [[nodiscard]] virtual status_t interruptableWriteFully(
-            FdTrigger *fdTrigger, const void *buf, size_t size,
+            FdTrigger *fdTrigger, iovec *iovs, size_t niovs,
             const std::function<status_t()> &altPoll) = 0;
     [[nodiscard]] virtual status_t interruptableReadFully(
-            FdTrigger *fdTrigger, void *buf, size_t size,
+            FdTrigger *fdTrigger, iovec *iovs, size_t niovs,
             const std::function<status_t()> &altPoll) = 0;
 
 protected:
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index ee46fcb..77493b3 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -32,17 +32,10 @@
     ],
 }
 
+// TODO(b/211908498): remove this
 cc_defaults {
     name: "libbinder_ndk_host_user",
     target: {
-        host: {
-            cflags: [
-                "-D__INTRODUCED_IN(n)=",
-                "-D__assert(a,b,c)=",
-                // We want all the APIs to be available on the host.
-                "-D__ANDROID_API__=10000",
-            ],
-        },
         darwin: {
             enabled: false,
         },
@@ -52,7 +45,6 @@
 cc_library {
     name: "libbinder_ndk",
 
-    defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
     recovery_available: true,
 
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 6949c2c..28e3ff4 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -799,3 +799,12 @@
 void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
     binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
 }
+
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
+    ABBinder* localBinder = binder->asABBinder();
+    if (localBinder == nullptr) {
+        LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
+    }
+
+    localBinder->setInheritRt(inheritRt);
+}
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 0ad400b..2c471c6 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -163,6 +163,15 @@
     const T get() const { return mT; }
 
     /**
+     * Release the underlying resource.
+     */
+    [[nodiscard]] T release() {
+        T a = mT;
+        mT = DEFAULT;
+        return a;
+    }
+
+    /**
      * This allows the value in this class to be set from beneath it. If you call this method and
      * then change the value of T*, you must take ownership of the value you are replacing and add
      * ownership to the object that is put in here.
@@ -365,6 +374,8 @@
     ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
     ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
 
+    ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); }
+
     bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); }
     bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); }
     bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); }
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 67623a6..0bf1e3d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -31,6 +31,7 @@
 #include <android/binder_internal_logging.h>
 #include <android/binder_parcel.h>
 
+#include <array>
 #include <optional>
 #include <string>
 #include <type_traits>
@@ -86,9 +87,87 @@
                                                         (is_specialization_v<T, std::optional> ||
                                                          is_specialization_v<T, std::unique_ptr>);
 
+// Tells if T is a fixed-size array.
+template <typename T>
+struct is_fixed_array : std::false_type {};
+
+template <typename T, size_t N>
+struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
+template <typename T>
+static inline constexpr bool dependent_false_v = false;
 }  // namespace
 
 /**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_stdArrayAllocator(void* arrayData, int32_t length, T** outBuffer) {
+    if (length < 0) return false;
+
+    if (length != static_cast<int32_t>(N)) {
+        return false;
+    }
+
+    std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+    *outBuffer = arr->data();
+    return true;
+}
+
+/**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayAllocator(void* arrayData, int32_t length,
+                                                     T** outBuffer) {
+    std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+    if (length < 0) {
+        *arr = std::nullopt;
+        return true;
+    }
+
+    if (length != static_cast<int32_t>(N)) {
+        return false;
+    }
+
+    arr->emplace();
+    *outBuffer = (*arr)->data();
+    return true;
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayExternalAllocator(void* arrayData, int32_t length) {
+    (void)arrayData;
+    return length == static_cast<int32_t>(N);
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayExternalAllocator(void* arrayData, int32_t length) {
+    std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+
+    if (length < 0) {
+        *arr = std::nullopt;
+        return true;
+    }
+
+    if (length != static_cast<int32_t>(N)) {
+        return false;
+    }
+
+    arr->emplace();
+    return true;
+}
+
+/**
  * This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
  */
 template <typename T>
@@ -397,6 +476,118 @@
 }
 
 /**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_stdArrayGetter(const void* arrData, size_t index) {
+    const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrData);
+    return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_stdArraySetter(void* arrData, size_t index, T value) {
+    std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrData);
+    (*arr)[index] = value;
+}
+
+/**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_nullableStdArrayGetter(const void* arrData, size_t index) {
+    const std::optional<std::array<T, N>>* arr =
+            static_cast<const std::optional<std::array<T, N>>*>(arrData);
+    return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_nullableStdArraySetter(void* arrData, size_t index, T value) {
+    std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrData);
+    (*arr)->at(index) = value;
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::string, N> at index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayStringElementAllocator(void* arrData, size_t index,
+                                                          int32_t length, char** buffer) {
+    std::array<std::string, N>* arr = static_cast<std::array<std::string, N>*>(arrData);
+    std::string& element = arr->at(index);
+    return AParcel_stdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a std::array<std::string, N> at index
+ * 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayStringElementGetter(const void* arrData, size_t index,
+                                                       int32_t* outLength) {
+    const std::array<std::string, N>* arr = static_cast<const std::array<std::string, N>*>(arrData);
+    const std::string& element = arr->at(index);
+
+    *outLength = static_cast<int32_t>(element.size());
+    return element.c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::optional<std::string>, N> at index 'index' to
+ * size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayNullableStringElementAllocator(void* arrData, size_t index,
+                                                                  int32_t length, char** buffer) {
+    std::array<std::optional<std::string>, N>* arr =
+            static_cast<std::array<std::optional<std::string>, N>*>(arrData);
+    std::optional<std::string>& element = arr->at(index);
+    return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a
+ * std::array<std::optional<std::string>, N> at index 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayNullableStringElementGetter(const void* arrData, size_t index,
+                                                               int32_t* outLength) {
+    const std::array<std::optional<std::string>, N>* arr =
+            static_cast<const std::array<std::optional<std::string>, N>*>(arrData);
+    const std::optional<std::string>& element = arr->at(index);
+
+    if (!element) {
+        *outLength = -1;
+        return nullptr;
+    }
+
+    *outLength = static_cast<int32_t>(element->size());
+    return element->c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::optional<std::array<std::optional<std::string>, N>> at
+ * index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_nullableStdArrayStringElementAllocator(void* arrData, size_t index,
+                                                                  int32_t length, char** buffer) {
+    std::optional<std::array<std::optional<std::string>, N>>* arr =
+            static_cast<std::optional<std::array<std::optional<std::string>, N>>*>(arrData);
+    std::optional<std::string>& element = (*arr)->at(index);
+    return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
  * Convenience API for writing a std::string.
  */
 static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
@@ -482,9 +673,7 @@
 template <typename P>
 static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
     if constexpr (is_interface_v<P>) {
-        if (!p) {
-            return STATUS_UNEXPECTED_NULL;
-        }
+        // Legacy behavior: allow null
         return first_template_type_t<P>::writeToParcel(parcel, p);
     } else {
         static_assert(is_parcelable_v<P>);
@@ -502,13 +691,8 @@
 template <typename P>
 static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
     if constexpr (is_interface_v<P>) {
-        binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p);
-        if (status == STATUS_OK) {
-            if (!*p) {
-                return STATUS_UNEXPECTED_NULL;
-            }
-        }
-        return status;
+        // Legacy behavior: allow null
+        return first_template_type_t<P>::readFromParcel(parcel, p);
     } else {
         static_assert(is_parcelable_v<P>);
         int32_t null;
@@ -560,7 +744,7 @@
             *p = std::nullopt;
             return STATUS_OK;
         }
-        *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{});
+        p->emplace(first_template_type_t<P>());
         return (*p)->readFromParcel(parcel);
     } else {
         static_assert(is_specialization_v<P, std::unique_ptr>);
@@ -578,6 +762,64 @@
     }
 }
 
+// Forward decls
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value);
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value);
+
+/**
+ * Reads an object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayData(const AParcel* parcel, void* arrayData, size_t index) {
+    std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+    return AParcel_readData(parcel, &arr->at(index));
+}
+
+/**
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayNullableData(const AParcel* parcel, void* arrayData,
+                                                 size_t index) {
+    std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+    return AParcel_readNullableData(parcel, &arr->at(index));
+}
+
+/**
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readNullableStdArrayNullableData(const AParcel* parcel, void* arrayData,
+                                                         size_t index) {
+    std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+    return AParcel_readNullableData(parcel, &(*arr)->at(index));
+}
+
+/**
+ * Writes an object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayData(AParcel* parcel, const void* arrayData, size_t index) {
+    const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+    return AParcel_writeData(parcel, arr->at(index));
+}
+
+/**
+ * Writes a nullable object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayNullableData(AParcel* parcel, const void* arrayData,
+                                                  size_t index) {
+    const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+    return AParcel_writeNullableData(parcel, arr->at(index));
+}
+
 /**
  * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
  */
@@ -721,9 +963,25 @@
  */
 template <typename P>
 static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
-    const void* vectorData = static_cast<const void*>(&vec);
-    return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
-                                        AParcel_writeStdVectorParcelableElement<P>);
+    if constexpr (std::is_enum_v<P>) {
+        if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+            return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+                                          static_cast<int32_t>(vec.size()));
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+            return AParcel_writeInt32Array(parcel, reinterpret_cast<const int32_t*>(vec.data()),
+                                           static_cast<int32_t>(vec.size()));
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+            return AParcel_writeInt64Array(parcel, reinterpret_cast<const int64_t*>(vec.data()),
+                                           static_cast<int32_t>(vec.size()));
+        } else {
+            static_assert(dependent_false_v<P>, "unrecognized type");
+        }
+    } else {
+        static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+        const void* vectorData = static_cast<const void*>(&vec);
+        return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
+                                            AParcel_writeStdVectorParcelableElement<P>);
+    }
 }
 
 /**
@@ -731,9 +989,24 @@
  */
 template <typename P>
 static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<P>* vec) {
-    void* vectorData = static_cast<void*>(vec);
-    return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<P>,
-                                       AParcel_readStdVectorParcelableElement<P>);
+    if constexpr (std::is_enum_v<P>) {
+        void* vectorData = static_cast<void*>(vec);
+        if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+            return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+            return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator<int32_t>);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+            return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator<int64_t>);
+        } else {
+            static_assert(dependent_false_v<P>, "unrecognized type");
+        }
+    } else {
+        static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+        void* vectorData = static_cast<void*>(vec);
+        return AParcel_readParcelableArray(parcel, vectorData,
+                                           AParcel_stdVectorExternalAllocator<P>,
+                                           AParcel_readStdVectorParcelableElement<P>);
+    }
 }
 
 /**
@@ -742,10 +1015,30 @@
 template <typename P>
 static inline binder_status_t AParcel_writeVector(AParcel* parcel,
                                                   const std::optional<std::vector<P>>& vec) {
-    if (!vec) return AParcel_writeInt32(parcel, -1);
-    const void* vectorData = static_cast<const void*>(&vec);
-    return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
-                                        AParcel_writeNullableStdVectorParcelableElement<P>);
+    if constexpr (std::is_enum_v<P>) {
+        if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+            return AParcel_writeByteArray(
+                    parcel, vec ? reinterpret_cast<const int8_t*>(vec->data()) : nullptr,
+                    vec ? static_cast<int32_t>(vec->size()) : -1);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+            return AParcel_writeInt32Array(
+                    parcel, vec ? reinterpret_cast<const int32_t*>(vec->data()) : nullptr,
+                    vec ? static_cast<int32_t>(vec->size()) : -1);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+            return AParcel_writeInt64Array(
+                    parcel, vec ? reinterpret_cast<const int64_t*>(vec->data()) : nullptr,
+                    vec ? static_cast<int32_t>(vec->size()) : -1);
+        } else {
+            static_assert(dependent_false_v<P>, "unrecognized type");
+        }
+    } else {
+        static_assert(!std::is_same_v<P, std::optional<std::string>>,
+                      "specialization should be used");
+        if (!vec) return AParcel_writeInt32(parcel, -1);
+        const void* vectorData = static_cast<const void*>(&vec);
+        return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
+                                            AParcel_writeNullableStdVectorParcelableElement<P>);
+    }
 }
 
 /**
@@ -754,10 +1047,28 @@
 template <typename P>
 static inline binder_status_t AParcel_readVector(const AParcel* parcel,
                                                  std::optional<std::vector<P>>* vec) {
-    void* vectorData = static_cast<void*>(vec);
-    return AParcel_readParcelableArray(parcel, vectorData,
-                                       AParcel_nullableStdVectorExternalAllocator<P>,
-                                       AParcel_readNullableStdVectorParcelableElement<P>);
+    if constexpr (std::is_enum_v<P>) {
+        void* vectorData = static_cast<void*>(vec);
+        if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+            return AParcel_readByteArray(parcel, vectorData,
+                                         AParcel_nullableStdVectorAllocator<int8_t>);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+            return AParcel_readInt32Array(parcel, vectorData,
+                                          AParcel_nullableStdVectorAllocator<int32_t>);
+        } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+            return AParcel_readInt64Array(parcel, vectorData,
+                                          AParcel_nullableStdVectorAllocator<int64_t>);
+        } else {
+            static_assert(dependent_false_v<P>, "unrecognized type");
+        }
+    } else {
+        static_assert(!std::is_same_v<P, std::optional<std::string>>,
+                      "specialization should be used");
+        void* vectorData = static_cast<void*>(vec);
+        return AParcel_readParcelableArray(parcel, vectorData,
+                                           AParcel_nullableStdVectorExternalAllocator<P>,
+                                           AParcel_readNullableStdVectorParcelableElement<P>);
+    }
 }
 
 // @START
@@ -1139,6 +1450,294 @@
     return STATUS_OK;
 }
 
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArray(AParcel* parcel,
+                                                      const std::array<T, N>& arr) {
+    if constexpr (std::is_same_v<T, bool>) {
+        const void* arrayData = static_cast<const void*>(&arr);
+        return AParcel_writeBoolArray(parcel, arrayData, static_cast<int32_t>(N),
+                                      &AParcel_stdArrayGetter<T, N>);
+    } else if constexpr (std::is_same_v<T, uint8_t>) {
+        return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(arr.data()),
+                                      static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, char16_t>) {
+        return AParcel_writeCharArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return AParcel_writeInt32Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return AParcel_writeInt64Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, float>) {
+        return AParcel_writeFloatArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, double>) {
+        return AParcel_writeDoubleArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+    } else if constexpr (std::is_same_v<T, std::string>) {
+        const void* arrayData = static_cast<const void*>(&arr);
+        return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+                                        &AParcel_stdArrayStringElementGetter<N>);
+    } else {
+        const void* arrayData = static_cast<const void*>(&arr);
+        return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+                                            &AParcel_writeStdArrayData<T, N>);
+    }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArrayWithNullableData(AParcel* parcel,
+                                                                      const std::array<T, N>& arr) {
+    if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+                  std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+                  std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+                  std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+        return AParcel_writeFixedArray(parcel, arr);
+    } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+        const void* arrayData = static_cast<const void*>(&arr);
+        return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+                                        &AParcel_stdArrayNullableStringElementGetter<N>);
+    } else {
+        const void* arrayData = static_cast<const void*>(&arr);
+        return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+                                            &AParcel_writeStdArrayNullableData<T, N>);
+    }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeNullableFixedArrayWithNullableData(
+        AParcel* parcel, const std::optional<std::array<T, N>>& arr) {
+    if (!arr) return AParcel_writeInt32(parcel, -1);
+    return AParcel_writeFixedArrayWithNullableData(parcel, arr.value());
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArray(const AParcel* parcel, std::array<T, N>* arr) {
+    void* arrayData = static_cast<void*>(arr);
+    if constexpr (std::is_same_v<T, bool>) {
+        return AParcel_readBoolArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+                                     &AParcel_stdArraySetter<T, N>);
+    } else if constexpr (std::is_same_v<T, uint8_t>) {
+        return AParcel_readByteArray(parcel, arrayData, &AParcel_stdArrayAllocator<int8_t, N>);
+    } else if constexpr (std::is_same_v<T, char16_t>) {
+        return AParcel_readCharArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return AParcel_readInt32Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return AParcel_readInt64Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, float>) {
+        return AParcel_readFloatArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, double>) {
+        return AParcel_readDoubleArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, std::string>) {
+        return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+                                       &AParcel_stdArrayStringElementAllocator<N>);
+    } else {
+        return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+                                           &AParcel_readStdArrayData<T, N>);
+    }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArrayWithNullableData(const AParcel* parcel,
+                                                                     std::array<T, N>* arr) {
+    void* arrayData = static_cast<void*>(arr);
+    if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+                  std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+                  std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+                  std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+        return AParcel_readFixedArray(parcel, arr);
+    } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+        return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+                                       &AParcel_stdArrayNullableStringElementAllocator<N>);
+    } else {
+        return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+                                           &AParcel_readStdArrayNullableData<T, N>);
+    }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readNullableFixedArrayWithNullableData(
+        const AParcel* parcel, std::optional<std::array<T, N>>* arr) {
+    void* arrayData = static_cast<void*>(arr);
+    if constexpr (std::is_same_v<T, bool>) {
+        return AParcel_readBoolArray(parcel, arrayData,
+                                     &AParcel_nullableStdArrayExternalAllocator<T, N>,
+                                     &AParcel_nullableStdArraySetter<T, N>);
+    } else if constexpr (std::is_same_v<T, uint8_t>) {
+        return AParcel_readByteArray(parcel, arrayData,
+                                     &AParcel_nullableStdArrayAllocator<int8_t, N>);
+    } else if constexpr (std::is_same_v<T, char16_t>) {
+        return AParcel_readCharArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return AParcel_readInt32Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return AParcel_readInt64Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, float>) {
+        return AParcel_readFloatArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, double>) {
+        return AParcel_readDoubleArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+    } else if constexpr (std::is_same_v<T, std::string>) {
+        return AParcel_readStringArray(parcel, arrayData,
+                                       &AParcel_nullableStdArrayExternalAllocator<N>,
+                                       &AParcel_nullableStdArrayStringElementAllocator<N>);
+    } else {
+        return AParcel_readParcelableArray(parcel, arrayData,
+                                           &AParcel_nullableStdArrayExternalAllocator<T, N>,
+                                           &AParcel_readStdArrayNullableData<T, N>);
+    }
+}
+
+/**
+ * Convenience API for writing a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) {
+    if constexpr (is_specialization_v<T, std::vector>) {
+        return AParcel_writeVector(parcel, value);
+    } else if constexpr (is_fixed_array_v<T>) {
+        return AParcel_writeFixedArray(parcel, value);
+    } else if constexpr (std::is_same_v<std::string, T>) {
+        return AParcel_writeString(parcel, value);
+    } else if constexpr (std::is_same_v<bool, T>) {
+        return AParcel_writeBool(parcel, value);
+    } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+        return AParcel_writeByte(parcel, value);
+    } else if constexpr (std::is_same_v<char16_t, T>) {
+        return AParcel_writeChar(parcel, value);
+    } else if constexpr (std::is_same_v<int32_t, T>) {
+        return AParcel_writeInt32(parcel, value);
+    } else if constexpr (std::is_same_v<int64_t, T>) {
+        return AParcel_writeInt64(parcel, value);
+    } else if constexpr (std::is_same_v<float, T>) {
+        return AParcel_writeFloat(parcel, value);
+    } else if constexpr (std::is_same_v<double, T>) {
+        return AParcel_writeDouble(parcel, value);
+    } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+        return AParcel_writeRequiredParcelFileDescriptor(parcel, value);
+    } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+        return AParcel_writeRequiredStrongBinder(parcel, value);
+    } else if constexpr (std::is_enum_v<T>) {
+        return AParcel_writeData(parcel, static_cast<std::underlying_type_t<T>>(value));
+    } else if constexpr (is_interface_v<T>) {
+        return AParcel_writeParcelable(parcel, value);
+    } else if constexpr (is_parcelable_v<T>) {
+        return AParcel_writeParcelable(parcel, value);
+    } else {
+        static_assert(dependent_false_v<T>, "unrecognized type");
+        return STATUS_OK;
+    }
+}
+
+/**
+ * Convenience API for writing a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value) {
+    if constexpr (is_specialization_v<T, std::optional> &&
+                  is_specialization_v<first_template_type_t<T>, std::vector>) {
+        return AParcel_writeVector(parcel, value);
+    } else if constexpr (is_specialization_v<T, std::optional> &&
+                         is_fixed_array_v<first_template_type_t<T>>) {
+        return AParcel_writeNullableFixedArrayWithNullableData(parcel, value);
+    } else if constexpr (is_fixed_array_v<T>) {  // happens with a nullable multi-dimensional array.
+        return AParcel_writeFixedArrayWithNullableData(parcel, value);
+    } else if constexpr (is_specialization_v<T, std::optional> &&
+                         std::is_same_v<first_template_type_t<T>, std::string>) {
+        return AParcel_writeString(parcel, value);
+    } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+        return AParcel_writeNullableParcelable(parcel, value);
+    } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+        return AParcel_writeNullableParcelFileDescriptor(parcel, value);
+    } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+        return AParcel_writeNullableStrongBinder(parcel, value);
+    } else {
+        return AParcel_writeData(parcel, value);
+    }
+}
+
+/**
+ * Convenience API for reading a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) {
+    if constexpr (is_specialization_v<T, std::vector>) {
+        return AParcel_readVector(parcel, value);
+    } else if constexpr (is_fixed_array_v<T>) {
+        return AParcel_readFixedArray(parcel, value);
+    } else if constexpr (std::is_same_v<std::string, T>) {
+        return AParcel_readString(parcel, value);
+    } else if constexpr (std::is_same_v<bool, T>) {
+        return AParcel_readBool(parcel, value);
+    } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+        return AParcel_readByte(parcel, value);
+    } else if constexpr (std::is_same_v<char16_t, T>) {
+        return AParcel_readChar(parcel, value);
+    } else if constexpr (std::is_same_v<int32_t, T>) {
+        return AParcel_readInt32(parcel, value);
+    } else if constexpr (std::is_same_v<int64_t, T>) {
+        return AParcel_readInt64(parcel, value);
+    } else if constexpr (std::is_same_v<float, T>) {
+        return AParcel_readFloat(parcel, value);
+    } else if constexpr (std::is_same_v<double, T>) {
+        return AParcel_readDouble(parcel, value);
+    } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+        return AParcel_readRequiredParcelFileDescriptor(parcel, value);
+    } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+        return AParcel_readRequiredStrongBinder(parcel, value);
+    } else if constexpr (std::is_enum_v<T>) {
+        return AParcel_readData(parcel, reinterpret_cast<std::underlying_type_t<T>*>(value));
+    } else if constexpr (is_interface_v<T>) {
+        return AParcel_readParcelable(parcel, value);
+    } else if constexpr (is_parcelable_v<T>) {
+        return AParcel_readParcelable(parcel, value);
+    } else {
+        static_assert(dependent_false_v<T>, "unrecognized type");
+        return STATUS_OK;
+    }
+}
+
+/**
+ * Convenience API for reading a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value) {
+    if constexpr (is_specialization_v<T, std::optional> &&
+                  is_specialization_v<first_template_type_t<T>, std::vector>) {
+        return AParcel_readVector(parcel, value);
+    } else if constexpr (is_specialization_v<T, std::optional> &&
+                         is_fixed_array_v<first_template_type_t<T>>) {
+        return AParcel_readNullableFixedArrayWithNullableData(parcel, value);
+    } else if constexpr (is_fixed_array_v<T>) {  // happens with a nullable multi-dimensional array.
+        return AParcel_readFixedArrayWithNullableData(parcel, value);
+    } else if constexpr (is_specialization_v<T, std::optional> &&
+                         std::is_same_v<first_template_type_t<T>, std::string>) {
+        return AParcel_readString(parcel, value);
+    } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+        return AParcel_readNullableParcelable(parcel, value);
+    } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+        return AParcel_readNullableParcelFileDescriptor(parcel, value);
+    } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+        return AParcel_readNullableStrongBinder(parcel, value);
+    } else {
+        return AParcel_readData(parcel, value);
+    }
+}
+
 }  // namespace ndk
 
 /** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index aa3b978..972eca7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -46,6 +46,18 @@
     AParcelableHolder() = delete;
     explicit AParcelableHolder(parcelable_stability_t stability)
         : mParcel(AParcel_create()), mStability(stability) {}
+
+#if __ANDROID_API__ >= 31
+    AParcelableHolder(const AParcelableHolder& other)
+        : mParcel(AParcel_create()), mStability(other.mStability) {
+        // AParcelableHolder has been introduced in 31.
+        if (__builtin_available(android 31, *)) {
+            AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
+                               AParcel_getDataSize(other.mParcel.get()));
+        }
+    }
+#endif
+
     AParcelableHolder(AParcelableHolder&& other) = default;
     virtual ~AParcelableHolder() = default;
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 6f1fdfc..76c7aac 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -32,11 +32,26 @@
 
 __BEGIN_DECLS
 
-#ifndef __ANDROID_API__
-#error Android builds must be compiled against a specific API. If this is an \
- android platform host build, you must use libbinder_ndk_host_user.
+#ifndef __BIONIC__
+
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(n)
 #endif
 
+#ifndef __assert
+#define __assert(a, b, c)          \
+    do {                           \
+        syslog(LOG_ERR, a ": " c); \
+        abort();                   \
+    } while (false)
+#endif
+
+#ifndef __ANDROID_API__
+#define __ANDROID_API__ 10000
+#endif
+
+#endif  // __BIONIC__
+
 /**
  * Low-level status types for use in binder. This is the least preferable way to
  * return an error for binder services (where binder_exception_t should be used,
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 b0217c4..89f21dd 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -68,4 +68,16 @@
  */
 void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) __INTRODUCED_IN(33);
 
+/**
+ * Allow the binder to inherit realtime scheduling policies from its caller.
+ *
+ * This must be called before the object is sent to another process. Not thread
+ * safe.
+ *
+ * \param binder local server binder to set the policy for
+ * \param inheritRt whether to inherit realtime scheduling policies (default is
+ *     false).
+ */
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) __INTRODUCED_IN(33);
+
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 197c0a1..3824a1b 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -146,6 +146,7 @@
     AIBinder_Class_disableInterfaceTokenHeader;
     AIBinder_DeathRecipient_setOnUnlinked;
     AIBinder_isHandlingTransaction;
+    AIBinder_setInheritRt; # llndk
     AIBinder_setMinSchedulerPolicy; # llndk
     AParcel_marshal;
     AParcel_unmarshal;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 499f88e..357b454 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -505,6 +505,31 @@
     }
 };
 
+TEST(NdkBinder, SetInheritRt) {
+    // functional test in binderLibTest
+    sp<IFoo> foo = sp<MyTestFoo>::make();
+    AIBinder* binder = foo->getBinder();
+
+    // does not abort
+    AIBinder_setInheritRt(binder, true);
+    AIBinder_setInheritRt(binder, false);
+    AIBinder_setInheritRt(binder, true);
+
+    AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, SetInheritRtNonLocal) {
+    AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+    ASSERT_NE(binder, nullptr);
+
+    ASSERT_TRUE(AIBinder_isRemote(binder));
+
+    EXPECT_DEATH(AIBinder_setInheritRt(binder, true), "");
+    EXPECT_DEATH(AIBinder_setInheritRt(binder, false), "");
+
+    AIBinder_decStrong(binder);
+}
+
 TEST(NdkBinder, AddNullService) {
     EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
 }
@@ -727,6 +752,29 @@
     ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
 }
 
+static void addOne(int* to) {
+    if (!to) return;
+    ++(*to);
+}
+struct FakeResource : public ndk::impl::ScopedAResource<int*, addOne, nullptr> {
+    explicit FakeResource(int* a) : ScopedAResource(a) {}
+};
+
+TEST(NdkBinder_ScopedAResource, GetDelete) {
+    int deleteCount = 0;
+    { FakeResource resource(&deleteCount); }
+    EXPECT_EQ(deleteCount, 1);
+}
+
+TEST(NdkBinder_ScopedAResource, Release) {
+    int deleteCount = 0;
+    {
+        FakeResource resource(&deleteCount);
+        (void)resource.release();
+    }
+    EXPECT_EQ(deleteCount, 0);
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 4561d6e..90cbf9d 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -24,13 +24,15 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
 }
 
 rust_library {
@@ -45,7 +47,7 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
     apex_available: [
         "//apex_available:platform",
@@ -69,13 +71,15 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
     lints: "none",
     clippy_lints: "none",
 }
@@ -88,20 +92,31 @@
     bindgen_flags: [
         // Unfortunately the only way to specify the rust_non_exhaustive enum
         // style for a type is to make it the default
-        "--default-enum-style", "rust_non_exhaustive",
+        "--default-enum-style",
+        "rust_non_exhaustive",
         // and then specify constified enums for the enums we don't want
         // rustified
-        "--constified-enum", "android::c_interface::consts::.*",
+        "--constified-enum",
+        "android::c_interface::consts::.*",
 
-        "--allowlist-type", "android::c_interface::.*",
-        "--allowlist-type", "AStatus",
-        "--allowlist-type", "AIBinder_Class",
-        "--allowlist-type", "AIBinder",
-        "--allowlist-type", "AIBinder_Weak",
-        "--allowlist-type", "AIBinder_DeathRecipient",
-        "--allowlist-type", "AParcel",
-        "--allowlist-type", "binder_status_t",
-        "--allowlist-function", ".*",
+        "--allowlist-type",
+        "android::c_interface::.*",
+        "--allowlist-type",
+        "AStatus",
+        "--allowlist-type",
+        "AIBinder_Class",
+        "--allowlist-type",
+        "AIBinder",
+        "--allowlist-type",
+        "AIBinder_Weak",
+        "--allowlist-type",
+        "AIBinder_DeathRecipient",
+        "--allowlist-type",
+        "AParcel",
+        "--allowlist-type",
+        "binder_status_t",
+        "--allowlist-function",
+        ".*",
     ],
     shared_libs: [
         "libbinder_ndk",
@@ -112,14 +127,6 @@
     // Currently necessary for host builds
     // TODO(b/31559095): bionic on host should define this
     target: {
-        host: {
-            cflags: [
-                "-D__INTRODUCED_IN(n)=",
-                "-D__assert(a,b,c)=",
-                // We want all the APIs to be available on the host.
-                "-D__ANDROID_API__=10000",
-            ],
-        },
         darwin: {
             enabled: false,
         },
@@ -127,8 +134,10 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
 }
 
 // TODO(b/184872979): remove once the Rust API is created.
@@ -142,8 +151,10 @@
     ],
     apex_available: [
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
 }
 
 rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 91047be..9dcef42 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -28,8 +28,8 @@
 //!
 //! [`Tokio`]: crate::Tokio
 
-use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
-use binder::{FromIBinder, StatusCode};
+use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
+use binder::binder_impl::BinderAsyncRuntime;
 use std::future::Future;
 
 /// Retrieve an existing service for a particular interface, sleeping for a few
@@ -37,12 +37,12 @@
 pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::get_interface::<T>(name);
+        return binder::get_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::get_interface::<T>(&name)
+        binder::get_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
@@ -61,12 +61,12 @@
 pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::wait_for_interface::<T>(name);
+        return binder::wait_for_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::wait_for_interface::<T>(&name)
+        binder::wait_for_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
@@ -120,3 +120,24 @@
         }
     }
 }
+
+/// Wrapper around Tokio runtime types for providing a runtime to a binder server.
+pub struct TokioRuntime<R>(pub R);
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Runtime> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<std::sync::Arc<tokio::runtime::Runtime>> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Handle> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index d09ac83..467e51e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -66,6 +66,35 @@
     }
 }
 
+/// Implemented by sync interfaces to specify what the associated async interface is.
+/// Generic to handle the fact that async interfaces are generic over a thread pool.
+///
+/// The binder in any object implementing this trait should be compatible with the
+/// `Target` associated type, and using `FromIBinder` to convert it to the target
+/// should not fail.
+pub trait ToAsyncInterface<P>
+where
+    Self: Interface,
+    Self::Target: FromIBinder,
+{
+    /// The async interface associated with this sync interface.
+    type Target: ?Sized;
+}
+
+/// Implemented by async interfaces to specify what the associated sync interface is.
+///
+/// The binder in any object implementing this trait should be compatible with the
+/// `Target` associated type, and using `FromIBinder` to convert it to the target
+/// should not fail.
+pub trait ToSyncInterface
+where
+    Self: Interface,
+    Self::Target: FromIBinder,
+{
+    /// The sync interface associated with this async interface.
+    type Target: ?Sized;
+}
+
 /// Interface stability promise
 ///
 /// An interface can promise to be a stable vendor interface ([`Vintf`]), or
@@ -163,9 +192,6 @@
     /// Is this object still alive?
     fn is_binder_alive(&self) -> bool;
 
-    /// Send a ping transaction to this object
-    fn ping_binder(&mut self) -> Result<()>;
-
     /// Indicate that the service intends to receive caller security contexts.
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool);
@@ -241,6 +267,9 @@
     /// The recipient will no longer be called if this object
     /// dies.
     fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+    /// Send a ping transaction to this object
+    fn ping_binder(&mut self) -> Result<()>;
 }
 
 /// Opaque reference to the type of a Binder interface.
@@ -337,6 +366,26 @@
     pub fn downgrade(this: &Strong<I>) -> Weak<I> {
         Weak::new(this)
     }
+
+    /// Convert this synchronous binder handle into an asynchronous one.
+    pub fn into_async<P>(self) -> Strong<<I as ToAsyncInterface<P>>::Target>
+    where
+        I: ToAsyncInterface<P>,
+    {
+        // By implementing the ToAsyncInterface trait, it is guaranteed that the binder
+        // object is also valid for the target type.
+        FromIBinder::try_from(self.0.as_binder()).unwrap()
+    }
+
+    /// Convert this asynchronous binder handle into a synchronous one.
+    pub fn into_sync(self) -> Strong<<I as ToSyncInterface>::Target>
+    where
+        I: ToSyncInterface,
+    {
+        // By implementing the ToSyncInterface trait, it is guaranteed that the binder
+        // object is also valid for the target type.
+        FromIBinder::try_from(self.0.as_binder()).unwrap()
+    }
 }
 
 impl<I: FromIBinder + ?Sized> Clone for Strong<I> {
@@ -487,13 +536,13 @@
 /// ```
 macro_rules! binder_fn_get_class {
     ($class:ty) => {
-        binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+        binder_fn_get_class!($crate::binder_impl::InterfaceClass::new::<$class>());
     };
 
     ($constructor:expr) => {
-        fn get_class() -> $crate::InterfaceClass {
+        fn get_class() -> $crate::binder_impl::InterfaceClass {
             static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-            static mut CLASS: Option<$crate::InterfaceClass> = None;
+            static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
             CLASS_INIT.call_once(|| unsafe {
                 // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
@@ -723,7 +772,7 @@
                 native: $native($on_transact),
                 proxy: $proxy {},
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -762,7 +811,7 @@
                     $($fname: $fty = $finit),*
                 },
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -779,9 +828,9 @@
     } => {
         $crate::declare_binder_interface! {
             $interface[$descriptor] {
-                @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+                @doc[concat!("A binder [`Remotable`]($crate::binder_impl::Remotable) that holds an [`", stringify!($interface), "`] object.")]
                 native: $native($on_transact),
-                @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+                @doc[concat!("A binder [`Proxy`]($crate::binder_impl::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
                 proxy: $proxy {
                     $($fname: $fty = $finit),*
                 },
@@ -818,7 +867,7 @@
             }
         }
 
-        impl $crate::Proxy for $proxy
+        impl $crate::binder_impl::Proxy for $proxy
         where
             $proxy: $interface,
         {
@@ -826,7 +875,7 @@
                 $descriptor
             }
 
-            fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+            fn from_binder(mut binder: $crate::SpIBinder) -> std::result::Result<Self, $crate::StatusCode> {
                 Ok(Self { binder, $($fname: $finit),* })
             }
         }
@@ -838,19 +887,19 @@
         impl $native {
             /// Create a new binder service.
             pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
-                let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+                let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
                 #[cfg(not(android_vndk))]
-                $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
+                $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
         }
 
-        impl $crate::Remotable for $native {
+        impl $crate::binder_impl::Remotable for $native {
             fn get_descriptor() -> &'static str {
                 $descriptor
             }
 
-            fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn on_transact(&self, code: $crate::binder_impl::TransactionCode, data: &$crate::binder_impl::BorrowedParcel<'_>, reply: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 match $on_transact(&*self.0, code, data, reply) {
                     // The C++ backend converts UNEXPECTED_NULL into an exception
                     Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -864,19 +913,19 @@
                 }
             }
 
-            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
+            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
                 self.0.dump(file, args)
             }
 
-            fn get_class() -> $crate::InterfaceClass {
+            fn get_class() -> $crate::binder_impl::InterfaceClass {
                 static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-                static mut CLASS: Option<$crate::InterfaceClass> = None;
+                static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
                 CLASS_INIT.call_once(|| unsafe {
                     // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
                     // variable, and therefore is thread-safe, as it can only occur
                     // once.
-                    CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+                    CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
                 });
                 unsafe {
                     // Safety: The `CLASS` variable can only be mutated once, above,
@@ -887,25 +936,25 @@
         }
 
         impl $crate::FromIBinder for dyn $interface {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $interface>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -913,7 +962,7 @@
                         return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -921,18 +970,18 @@
             }
         }
 
-        impl $crate::parcel::Serialize for dyn $interface + '_
+        impl $crate::binder_impl::Serialize for dyn $interface + '_
         where
             dyn $interface: $crate::Interface
         {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl $crate::parcel::SerializeOption for dyn $interface + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeOption for dyn $interface + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -955,25 +1004,25 @@
         $(
         // Async interface trait implementations.
         impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -982,7 +1031,7 @@
                         //return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -990,15 +1039,15 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -1017,6 +1066,14 @@
                     .expect(concat!("Error cloning interface ", stringify!($async_interface)))
             }
         }
+
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
+            type Target = dyn $async_interface<P>;
+        }
+
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
+            type Target = dyn $interface;
+        }
         )?
     };
 }
@@ -1027,44 +1084,48 @@
 #[macro_export]
 macro_rules! declare_binder_enum {
     {
+        $( #[$attr:meta] )*
         $enum:ident : [$backing:ty; $size:expr] {
             $( $name:ident = $value:expr, )*
         }
     } => {
+        $( #[$attr] )*
         #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+        #[allow(missing_docs)]
         pub struct $enum(pub $backing);
         impl $enum {
-            $( pub const $name: Self = Self($value); )*
+            $( #[allow(missing_docs)] pub const $name: Self = Self($value); )*
 
             #[inline(always)]
+            #[allow(missing_docs)]
             pub const fn enum_values() -> [Self; $size] {
                 [$(Self::$name),*]
             }
         }
 
-        impl $crate::parcel::Serialize for $enum {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::Serialize for $enum {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&self.0)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $enum {
-            fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeArray for $enum {
+            fn serialize_array(slice: &[Self], parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
-                <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+                <$backing as $crate::binder_impl::SerializeArray>::serialize_array(&v[..], parcel)
             }
         }
 
-        impl $crate::parcel::Deserialize for $enum {
-            fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
+        impl $crate::binder_impl::Deserialize for $enum {
+            fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
                 parcel.read().map(Self)
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $enum {
-            fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
+        impl $crate::binder_impl::DeserializeArray for $enum {
+            fn deserialize_array(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Option<Vec<Self>>, $crate::StatusCode> {
                 let v: Option<Vec<$backing>> =
-                    <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+                    <$backing as $crate::binder_impl::DeserializeArray>::deserialize_array(parcel)?;
                 Ok(v.map(|v| v.into_iter().map(Self).collect()))
             }
         }
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
index 214c0b5..579f9f9 100644
--- a/libs/binder/rust/src/binder_async.rs
+++ b/libs/binder/rust/src/binder_async.rs
@@ -53,3 +53,9 @@
         B: Send + 'a,
         E: From<crate::StatusCode>;
 }
+
+/// A runtime for executing an async binder server.
+pub trait BinderAsyncRuntime {
+    /// Block on the provided future, running it to completion and returning its output.
+    fn block_on<F: Future>(&self, future: F) -> F::Output;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index b94dfa1..1d7de98 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,45 +101,50 @@
 mod binder_async;
 mod error;
 mod native;
+mod parcel;
 mod state;
 
 use binder_ndk_sys as sys;
 
-pub mod parcel;
-
-pub use crate::binder::{
-    BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
-    Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION,
-    FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
+pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
+pub use error::{ExceptionCode, Status, StatusCode};
+pub use native::{
+    add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
 };
-pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
-pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
-pub use parcel::{BorrowedParcel, Parcel};
-pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
-pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+pub use proxy::{
+    get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
+    WpIBinder,
+};
 pub use state::{ProcessState, ThreadState};
 
+/// Binder result containing a [`Status`] on error.
+pub type Result<T> = std::result::Result<T, Status>;
+
+/// Advanced Binder APIs needed internally by AIDL or when manually using Binder
+/// without AIDL.
+pub mod binder_impl {
+    pub use crate::binder::{
+        IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
+        TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+        FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+    };
+    pub use crate::binder_async::BinderAsyncRuntime;
+    pub use crate::error::status_t;
+    pub use crate::native::Binder;
+    pub use crate::parcel::{
+        BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
+        ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
+        NULL_PARCELABLE_FLAG,
+    };
+    pub use crate::proxy::{AssociateClass, Proxy};
+}
+
 /// Unstable, in-development API that only allowlisted clients are allowed to use.
+#[doc(hidden)]
 pub mod unstable_api {
     pub use crate::binder::AsNative;
     pub use crate::proxy::unstable_api::new_spibinder;
     pub use crate::sys::AIBinder;
 }
-
-/// The public API usable outside AIDL-generated interface crates.
-pub mod public_api {
-    pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
-    pub use super::{
-        add_service, force_lazy_services_persist, get_interface, register_lazy_service,
-        wait_for_interface,
-    };
-    pub use super::{
-        BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
-        Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
-        WpIBinder,
-    };
-
-    /// Binder result containing a [`Status`] on error.
-    pub type Result<T> = std::result::Result<T, Status>;
-}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 206b90c..256fa8b 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -496,7 +496,7 @@
     {
         let start = self.get_data_position();
         let parcelable_size: i32 = self.read()?;
-        if parcelable_size < 0 {
+        if parcelable_size < 4 {
             return Err(StatusCode::BAD_VALUE);
         }
 
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index b0dea94..4d3d59a 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -60,6 +60,16 @@
     }
 }
 
+impl PartialEq for ParcelFileDescriptor {
+    // Since ParcelFileDescriptors own the FD, if this function ever returns true (and it is used to
+    // compare two different objects), then it would imply that an FD is double-owned.
+    fn eq(&self, other: &Self) -> bool {
+        self.as_raw_fd() == other.as_raw_fd()
+    }
+}
+
+impl Eq for ParcelFileDescriptor {}
+
 impl Serialize for ParcelFileDescriptor {
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
         let fd = self.0.as_raw_fd();
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 9007cba..0c7e48d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -652,6 +652,39 @@
     }
 }
 
+impl<T: SerializeArray, const N: usize> Serialize for [T; N] {
+    fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        // forwards to T::serialize_array.
+        SerializeArray::serialize_array(self, parcel)
+    }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeOption for [T; N] {
+    fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        SerializeOption::serialize_option(this.map(|arr| &arr[..]), parcel)
+    }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {}
+
+impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] {
+    fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
+        let vec = DeserializeArray::deserialize_array(parcel)
+            .transpose()
+            .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))?;
+        vec.try_into().or(Err(StatusCode::BAD_VALUE))
+    }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeOption for [T; N] {
+    fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
+        let vec = DeserializeArray::deserialize_array(parcel)?;
+        vec.map(|v| v.try_into().or(Err(StatusCode::BAD_VALUE))).transpose()
+    }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeArray for [T; N] {}
+
 impl Serialize for Stability {
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
         i32::from(*self).serialize(parcel)
@@ -769,35 +802,32 @@
 #[macro_export]
 macro_rules! impl_serialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Serialize for $parcelable {
+        impl $crate::binder_impl::Serialize for $parcelable {
             fn serialize(
                 &self,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
-                <Self as $crate::parcel::SerializeOption>::serialize_option(
-                    Some(self),
-                    parcel,
-                )
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
+                <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $parcelable {}
+        impl $crate::binder_impl::SerializeArray for $parcelable {}
 
-        impl $crate::parcel::SerializeOption for $parcelable {
+        impl $crate::binder_impl::SerializeOption for $parcelable {
             fn serialize_option(
                 this: Option<&Self>,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 if let Some(this) = this {
-                    use $crate::parcel::Parcelable;
-                    parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+                    use $crate::Parcelable;
+                    parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
                     this.write_to_parcel(parcel)
                 } else {
-                    parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+                    parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
                 }
             }
         }
-    }
+    };
 }
 
 /// Implement `Deserialize` trait and friends for a parcelable
@@ -809,54 +839,54 @@
 #[macro_export]
 macro_rules! impl_deserialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Deserialize for $parcelable {
+        impl $crate::binder_impl::Deserialize for $parcelable {
             fn deserialize(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Self> {
-                $crate::parcel::DeserializeOption::deserialize_option(parcel)
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Self, $crate::StatusCode> {
+                $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
                     .transpose()
                     .unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
             }
             fn deserialize_from(
                 &mut self,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     Err($crate::StatusCode::UNEXPECTED_NULL)
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     self.read_from_parcel(parcel)
                 }
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $parcelable {}
+        impl $crate::binder_impl::DeserializeArray for $parcelable {}
 
-        impl $crate::parcel::DeserializeOption for $parcelable {
+        impl $crate::binder_impl::DeserializeOption for $parcelable {
             fn deserialize_option(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Option<Self>> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
                 let mut result = None;
                 Self::deserialize_option_from(&mut result, parcel)?;
                 Ok(result)
             }
             fn deserialize_option_from(
                 this: &mut Option<Self>,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     *this = None;
                     Ok(())
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     this.get_or_insert_with(Self::default)
                         .read_from_parcel(parcel)
                 }
             }
         }
-    }
+    };
 }
 
 impl<T: Serialize> Serialize for Box<T> {
@@ -885,7 +915,7 @@
 
 #[cfg(test)]
 mod tests {
-    use crate::Parcel;
+    use crate::parcel::Parcel;
     use super::*;
 
     #[test]
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index b4282b2..d58e839 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,9 +15,11 @@
  */
 
 use crate::binder::Stability;
-use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
-use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+use crate::error::StatusCode;
+use crate::parcel::{
+    BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
+    NULL_PARCELABLE_FLAG,
+};
 
 use downcast_rs::{impl_downcast, DowncastSync};
 use std::any::Any;
@@ -53,12 +55,6 @@
     Parcel(Parcel),
 }
 
-impl Default for ParcelableHolderData {
-    fn default() -> Self {
-        ParcelableHolderData::Empty
-    }
-}
-
 /// A container that can hold any arbitrary `Parcelable`.
 ///
 /// This type is currently used for AIDL parcelable fields.
@@ -66,7 +62,7 @@
 /// `ParcelableHolder` is currently not thread-safe (neither
 /// `Send` nor `Sync`), mainly because it internally contains
 /// a `Parcel` which in turn is not thread-safe.
-#[derive(Debug, Default)]
+#[derive(Debug)]
 pub struct ParcelableHolder {
     // This is a `Mutex` because of `get_parcelable`
     // which takes `&self` for consistency with C++.
@@ -97,7 +93,7 @@
     }
 
     /// Set the parcelable contained in this `ParcelableHolder`.
-    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<(), StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
     {
@@ -126,7 +122,7 @@
     /// * `Ok(None)` if the holder is empty or the descriptor does not match
     /// * `Ok(Some(_))` if the object holds a parcelable of type `T`
     ///   with the correct descriptor
-    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>, StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
     {
@@ -176,11 +172,28 @@
     }
 }
 
-impl_serialize_for_parcelable!(ParcelableHolder);
-impl_deserialize_for_parcelable!(ParcelableHolder);
+impl Serialize for ParcelableHolder {
+    fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
+        parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+        self.write_to_parcel(parcel)
+    }
+}
+
+impl Deserialize for ParcelableHolder {
+    fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
+        let status: i32 = parcel.read()?;
+        if status == NULL_PARCELABLE_FLAG {
+            Err(StatusCode::UNEXPECTED_NULL)
+        } else {
+            let mut parcelable = ParcelableHolder::new(Default::default());
+            parcelable.read_from_parcel(parcel)?;
+            Ok(parcelable)
+        }
+    }
+}
 
 impl Parcelable for ParcelableHolder {
-    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
         parcel.write(&self.stability)?;
 
         let mut data = self.data.lock().unwrap();
@@ -219,7 +232,7 @@
         }
     }
 
-    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
         self.stability = parcel.read()?;
 
         let data_size: i32 = parcel.read()?;
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 760d862..12bfde7 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -312,17 +312,6 @@
         }
     }
 
-    fn ping_binder(&mut self) -> Result<()> {
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`.
-            //
-            // This call does not affect ownership of its pointer parameter.
-            sys::AIBinder_ping(self.as_native_mut())
-        };
-        status_result(status)
-    }
-
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool) {
         unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
@@ -412,6 +401,17 @@
             )
         })
     }
+
+    fn ping_binder(&mut self) -> Result<()> {
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`.
+            //
+            // This call does not affect ownership of its pointer parameter.
+            sys::AIBinder_ping(self.as_native_mut())
+        };
+        status_result(status)
+    }
 }
 
 impl Serialize for SpIBinder {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 1fd2ead..50daf1c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -16,12 +16,14 @@
 
 //! Rust Binder crate integration tests
 
-use binder::declare_binder_interface;
-use binder::parcel::BorrowedParcel;
-use binder::{
-    Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
-    FIRST_CALL_TRANSACTION,
+use binder::{declare_binder_enum, declare_binder_interface};
+use binder::{BinderFeatures, Interface, StatusCode, ThreadState};
+// Import from internal API for testing only, do not use this module in
+// production.
+use binder::binder_impl::{
+    Binder, BorrowedParcel, IBinderInternal, TransactionCode, FIRST_CALL_TRANSACTION,
 };
+
 use std::convert::{TryFrom, TryInto};
 use std::ffi::CStr;
 use std::fs::File;
@@ -100,6 +102,7 @@
     Test = FIRST_CALL_TRANSACTION,
     GetDumpArgs,
     GetSelinuxContext,
+    GetIsHandlingTransaction,
 }
 
 impl TryFrom<u32> for TestTransactionCode {
@@ -112,13 +115,14 @@
             _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
                 Ok(TestTransactionCode::GetSelinuxContext)
             }
+            _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction),
             _ => Err(StatusCode::UNKNOWN_TRANSACTION),
         }
     }
 }
 
 impl Interface for TestService {
-    fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
+    fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
         let mut dump_args = self.dump_args.lock().unwrap();
         dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
         Ok(())
@@ -126,44 +130,54 @@
 }
 
 impl ITest for TestService {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         Ok(self.s.clone())
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let args = self.dump_args.lock().unwrap().clone();
         Ok(args)
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let sid =
             ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
         sid.ok_or(StatusCode::UNEXPECTED_NULL)
     }
+
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
+        Ok(binder::is_handling_transaction())
+    }
 }
 
 /// Trivial testing binder interface
 pub trait ITest: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::Result<String>;
+    fn test(&self) -> Result<String, StatusCode>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::Result<Vec<String>>;
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::Result<String>;
+    fn get_selinux_context(&self) -> Result<String, StatusCode>;
+
+    /// Returns the value of calling `is_handling_transaction`.
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode>;
 }
 
 /// Async trivial testing binder interface
 pub trait IATest<P>: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
+
+    /// Returns the value of calling `is_handling_transaction`.
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>>;
 }
 
 declare_binder_interface! {
@@ -181,30 +195,31 @@
     code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     match code.try_into()? {
         TestTransactionCode::Test => reply.write(&service.test()?),
         TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
         TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+        TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?),
     }
 }
 
 impl ITest for BpTest {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let reply = self.binder.transact(
             TestTransactionCode::GetSelinuxContext as TransactionCode,
             0,
@@ -212,10 +227,19 @@
         )?;
         reply.read()
     }
+
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
+        let reply = self.binder.transact(
+            TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
+            0,
+            |_| Ok(()),
+        )?;
+        reply.read()
+    }
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
@@ -223,7 +247,7 @@
         )
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
@@ -231,44 +255,61 @@
         )
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
             |reply| async move { reply?.read() }
         )
     }
+
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
+        let binder = self.binder.clone();
+        P::spawn(
+            move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
+            |reply| async move { reply?.read() }
+        )
+    }
 }
 
 impl ITest for Binder<BnTest> {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         self.0.test()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         self.0.get_dump_args()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         self.0.get_selinux_context()
     }
+
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
+        self.0.get_is_handling_transaction()
+    }
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.test();
         Box::pin(async move { res })
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let res = self.0.get_dump_args();
         Box::pin(async move { res })
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.get_selinux_context();
         Box::pin(async move { res })
     }
+
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
+        let res = self.0.get_is_handling_transaction();
+        Box::pin(async move { res })
+    }
 }
 
 /// Trivial testing binder interface
@@ -286,7 +327,7 @@
     _code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     _reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     Ok(())
 }
 
@@ -294,6 +335,23 @@
 
 impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
 
+declare_binder_enum! {
+    TestEnum : [i32; 3] {
+        FOO = 1,
+        BAR = 2,
+        BAZ = 3,
+    }
+}
+
+declare_binder_enum! {
+    #[deprecated(since = "1.0.0")]
+    TestDeprecatedEnum : [i32; 3] {
+        FOO = 1,
+        BAR = 2,
+        BAZ = 3,
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use selinux_bindgen as selinux_sys;
@@ -307,9 +365,12 @@
     use std::time::Duration;
 
     use binder::{
-        Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
-        SpIBinder, StatusCode, Strong,
+        BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
+        Strong,
     };
+    // Import from impl API for testing only, should not be necessary as long as
+    // you are using AIDL.
+    use binder::binder_impl::{Binder, IBinderInternal, TransactionCode};
 
     use binder_tokio::Tokio;
 
@@ -483,7 +544,7 @@
 
     #[tokio::test]
     async fn get_selinux_context_async() {
-        let service_name = "get_selinux_context";
+        let service_name = "get_selinux_context_async";
         let _process = ScopedServiceProcess::new(service_name);
         let test_client: Strong<dyn IATest<Tokio>> =
             binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
@@ -493,6 +554,32 @@
         );
     }
 
+    #[tokio::test]
+    async fn get_selinux_context_sync_to_async() {
+        let service_name = "get_selinux_context";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Strong<dyn ITest> =
+            binder::get_interface(service_name).expect("Did not get manager binder service");
+        let test_client = test_client.into_async::<Tokio>();
+        assert_eq!(
+            test_client.get_selinux_context().await.unwrap(),
+            get_expected_selinux_context()
+        );
+    }
+
+    #[tokio::test]
+    async fn get_selinux_context_async_to_sync() {
+        let service_name = "get_selinux_context";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Strong<dyn IATest<Tokio>> =
+            binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+        let test_client = test_client.into_sync();
+        assert_eq!(
+            test_client.get_selinux_context().unwrap(),
+            get_expected_selinux_context()
+        );
+    }
+
     struct Bools {
         binder_died: Arc<AtomicBool>,
         binder_dealloc: Arc<AtomicBool>,
@@ -661,8 +748,7 @@
             let _process = ScopedServiceProcess::new(service_name);
 
             let test_client: Strong<dyn ITest> =
-                binder::get_interface(service_name)
-                .expect("Did not get test binder service");
+                binder::get_interface(service_name).expect("Did not get test binder service");
             let mut remote = test_client.as_binder();
             assert!(remote.is_binder_alive());
             remote.ping_binder().expect("Could not ping remote service");
@@ -843,11 +929,52 @@
         let service2 = service2.as_binder();
 
         let parcel = service1.prepare_transact().unwrap();
-        let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+        let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0);
 
         match res {
             Ok(_) => panic!("submit_transact should fail"),
             Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE),
         }
     }
+
+    #[test]
+    fn get_is_handling_transaction() {
+        let service_name = "get_is_handling_transaction";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Strong<dyn ITest> =
+            binder::get_interface(service_name).expect("Did not get manager binder service");
+        // Should be true externally.
+        assert!(test_client.get_is_handling_transaction().unwrap());
+
+        // Should be false locally.
+        assert!(!binder::is_handling_transaction());
+
+        // Should also be false in spawned thread.
+        std::thread::spawn(|| {
+            assert!(!binder::is_handling_transaction());
+        }).join().unwrap();
+    }
+
+    #[tokio::test]
+    async fn get_is_handling_transaction_async() {
+        let service_name = "get_is_handling_transaction_async";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Strong<dyn IATest<Tokio>> =
+            binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+        // Should be true externally.
+        assert!(test_client.get_is_handling_transaction().await.unwrap());
+
+        // Should be false locally.
+        assert!(!binder::is_handling_transaction());
+
+        // Should also be false in spawned task.
+        tokio::spawn(async {
+            assert!(!binder::is_handling_transaction());
+        }).await.unwrap();
+
+        // And in spawn_blocking task.
+        tokio::task::spawn_blocking(|| {
+            assert!(!binder::is_handling_transaction());
+        }).await.unwrap();
+    }
 }
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 1fc761e..b62da7b 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,12 @@
 //! access.
 
 use binder::declare_binder_interface;
-use binder::parcel::ParcelFileDescriptor;
 use binder::{
-    Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
-    StatusCode, TransactionCode,
+    BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode,
 };
+// Import from impl API for testing only, should not be necessary as long as you
+// are using AIDL.
+use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode};
 
 use std::ffi::{c_void, CStr, CString};
 use std::sync::Once;
@@ -113,7 +114,7 @@
     code: TransactionCode,
     parcel: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> Result<()> {
+) -> Result<(), StatusCode> {
     match code {
         bindings::Transaction_TEST_BOOL => {
             assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 4d316f7..63a4b2c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -112,7 +112,7 @@
     BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
     BINDER_LIB_TEST_GETPID,
     BINDER_LIB_TEST_ECHO_VECTOR,
-    BINDER_LIB_TEST_REJECT_BUF,
+    BINDER_LIB_TEST_REJECT_OBJECTS,
     BINDER_LIB_TEST_CAN_GET_SID,
 };
 
@@ -496,7 +496,7 @@
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
     EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 
-    bool sync_received, async_received;
+    uint32_t sync_received, async_received;
 
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
                 &async_received));
@@ -504,15 +504,7 @@
     EXPECT_EQ(sync_received, 1);
     EXPECT_EQ(async_received, 0);
 
-    uint32_t sync_received2, async_received2;
-
-    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
-                &async_received2));
-
-    EXPECT_EQ(sync_received2, 1);
-    EXPECT_EQ(async_received2, 0);
-
-    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
     EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 }
 
@@ -1174,13 +1166,53 @@
     memcpy(parcelData, &obj, sizeof(obj));
     data.setDataSize(sizeof(obj));
 
+    EXPECT_EQ(data.objectsCount(), 1);
+
     // Either the kernel should reject this transaction (if it's correct), but
     // if it's not, the server implementation should return an error if it
     // finds an object in the received Parcel.
-    EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
                 Not(StatusEq(NO_ERROR)));
 }
 
+TEST_F(BinderLibTest, WeakRejected) {
+    Parcel data, reply;
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    auto binder = sp<BBinder>::make();
+    wp<BBinder> wpBinder(binder);
+    flat_binder_object obj{
+            .hdr = {.type = BINDER_TYPE_WEAK_BINDER},
+            .flags = 0,
+            .binder = reinterpret_cast<uintptr_t>(wpBinder.get_refs()),
+            .cookie = reinterpret_cast<uintptr_t>(wpBinder.unsafe_get()),
+    };
+    data.setDataCapacity(1024);
+    // Write a bogus object at offset 0 to get an entry in the offset table
+    data.writeFileDescriptor(0);
+    EXPECT_EQ(data.objectsCount(), 1);
+    uint8_t *parcelData = const_cast<uint8_t *>(data.data());
+    // And now, overwrite it with the weak binder
+    memcpy(parcelData, &obj, sizeof(obj));
+    data.setDataSize(sizeof(obj));
+
+    // a previous bug caused other objects to be released an extra time, so we
+    // test with an object that libbinder will actually try to release
+    EXPECT_EQ(OK, data.writeStrongBinder(sp<BBinder>::make()));
+
+    EXPECT_EQ(data.objectsCount(), 2);
+
+    // send it many times, since previous error was memory corruption, make it
+    // more likely that the server crashes
+    for (size_t i = 0; i < 100; i++) {
+        EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
+                    StatusEq(BAD_VALUE));
+    }
+
+    EXPECT_THAT(server->pingBinder(), StatusEq(NO_ERROR));
+}
+
 TEST_F(BinderLibTest, GotSid) {
     sp<IBinder> server = addServer();
 
@@ -1574,7 +1606,7 @@
                 reply->writeUint64Vector(vector);
                 return NO_ERROR;
             }
-            case BINDER_LIB_TEST_REJECT_BUF: {
+            case BINDER_LIB_TEST_REJECT_OBJECTS: {
                 return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
             }
             case BINDER_LIB_TEST_CAN_GET_SID: {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 4950b23..aee15d8 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -16,15 +16,17 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
+#include <binder/Status.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 
 using android::IPCThreadState;
 using android::OK;
 using android::Parcel;
+using android::status_t;
 using android::String16;
 using android::String8;
-using android::status_t;
+using android::binder::Status;
 
 TEST(Parcel, NonNullTerminatedString8) {
     String8 kTestString = String8("test-is-good");
@@ -60,6 +62,19 @@
     EXPECT_EQ(output.size(), 0);
 }
 
+TEST(Parcel, EnforceNoDataAvail) {
+    const int32_t kTestInt = 42;
+    const String8 kTestString = String8("test-is-good");
+    Parcel p;
+    p.writeInt32(kTestInt);
+    p.writeString8(kTestString);
+    p.setDataPosition(0);
+    EXPECT_EQ(kTestInt, p.readInt32());
+    EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_BAD_PARCELABLE);
+    EXPECT_EQ(kTestString, p.readString8());
+    EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE);
+}
+
 // Tests a second operation results in a parcel at the same location as it
 // started.
 void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5a96b78..c2639e7 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1517,10 +1517,11 @@
     auto keepAlive = sp<BBinder>::make();
     auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
 
-    if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+    if (!android::base::GetBoolProperty("ro.debuggable", false) ||
+        android::base::GetProperty("ro.build.type", "") == "user") {
         ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
-                << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, "
-                   "but get "
+                << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user "
+                   "builds, but get "
                 << statusToString(setRpcClientDebugStatus);
         GTEST_SKIP();
     }
@@ -1674,8 +1675,8 @@
         static AssertionResult defaultPostConnect(RpcTransport* serverTransport,
                                                   FdTrigger* fdTrigger) {
             std::string message(kMessage);
-            auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
-                                                                   message.size(), {});
+            iovec messageIov{message.data(), message.size()};
+            auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
             if (status != OK) return AssertionFailure() << statusToString(status);
             return AssertionSuccess();
         }
@@ -1706,9 +1707,9 @@
         AssertionResult readMessage(const std::string& expectedMessage = kMessage) {
             LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed");
             std::string readMessage(expectedMessage.size(), '\0');
-            status_t readStatus =
-                    mClientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
-                                                             readMessage.size(), {});
+            iovec readMessageIov{readMessage.data(), readMessage.size()};
+            status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(),
+                                                                           &readMessageIov, 1, {});
             if (readStatus != OK) {
                 return AssertionFailure() << statusToString(readStatus);
             }
@@ -1902,8 +1903,8 @@
     bool shouldContinueWriting = false;
     auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
         std::string message(RpcTransportTestUtils::kMessage);
-        auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
-                                                               message.size(), {});
+        iovec messageIov{message.data(), message.size()};
+        auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
         if (status != OK) return AssertionFailure() << statusToString(status);
 
         {
@@ -1913,7 +1914,8 @@
             }
         }
 
-        status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size(), {});
+        iovec msg2Iov{msg2.data(), msg2.size()};
+        status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {});
         if (status != DEAD_OBJECT)
             return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
                                          "should return DEAD_OBJECT, but it is "
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index acf3f8f..1446802 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -9,7 +9,6 @@
 
 cc_fuzz {
     name: "binder_parcel_fuzzer",
-    defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
 
     fuzz_config: {
@@ -50,6 +49,9 @@
                 "libbinder",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
     },
     // This flag enables verbose output in the fuzz target, and is very useful
     // for debugging a failure. If you are trying to diagnose how a crash was
@@ -63,7 +65,7 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
     srcs: [
         "random_fd.cpp",
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 32406e5..13f7195 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -22,6 +22,7 @@
 #include <android/os/IServiceManager.h>
 #include <binder/ParcelableHolder.h>
 #include <binder/PersistableBundle.h>
+#include <binder/Status.h>
 
 using ::android::status_t;
 using ::android::base::HexString;
@@ -100,6 +101,7 @@
     PARCEL_READ_NO_STATUS(size_t, dataAvail),
     PARCEL_READ_NO_STATUS(size_t, dataPosition),
     PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+    PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail),
     [] (const ::android::Parcel& p, uint8_t pos) {
         FUZZ_LOG() << "about to setDataPosition: " << pos;
         p.setDataPosition(pos);
@@ -233,6 +235,32 @@
     PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
     PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
 
+#define COMMA ,
+    PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray),
+    // nested arrays
+    PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray),
+#undef COMMA
+
     [] (const android::Parcel& p, uint8_t /*len*/) {
         FUZZ_LOG() << "about to read flattenable";
         ExampleFlattenable f;
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 752fcbb..5aeb5cc 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -156,5 +156,21 @@
         PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
         PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
         PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+
+        // methods for std::array<T,N>
+#define COMMA ,
+        PARCEL_READ(std::array<bool COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<uint8_t COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<char16_t COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<int32_t COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<int64_t COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<float COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<double COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<std::string COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<SomeParcelable COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<ndk::SpAIBinder COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
+        PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
+#undef COMMA
 };
 // clang-format on
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 570af71..73f9d4d 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -12,8 +12,9 @@
     srcs: ["cputimeinstate.cpp"],
     shared_libs: [
         "libbase",
-        "libbpf",
+        "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "liblog",
         "libnetdutils"
     ],
@@ -31,8 +32,9 @@
     srcs: ["testtimeinstate.cpp"],
     shared_libs: [
         "libbase",
-        "libbpf",
+        "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "libtimeinstate",
         "libnetdutils",
     ],
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index f2d0943..858739c 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -88,6 +88,7 @@
     if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
     if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
     if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
+    if ((status = parcel->writeBool(angleInUse)) != OK) return status;
     return OK;
 }
 
@@ -101,6 +102,7 @@
     if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
     if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
     if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
+    if ((status = parcel->readBool(&angleInUse)) != OK) return status;
     return OK;
 }
 
@@ -111,6 +113,7 @@
     StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
     StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
     StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
+    StringAppendF(&result, "angleInUse = %d\n", angleInUse);
     result.append("glDriverLoadingTime:");
     for (int32_t loadingTime : glDriverLoadingTime) {
         StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d54de49..7f0cac5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -343,80 +343,6 @@
     return nullptr;
 }
 
-bool GraphicsEnv::checkAngleRules(void* so) {
-    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
-    auto model = base::GetProperty("ro.product.model", "UNSET");
-
-    auto ANGLEGetFeatureSupportUtilAPIVersion =
-            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
-                                                          "ANGLEGetFeatureSupportUtilAPIVersion");
-
-    if (!ANGLEGetFeatureSupportUtilAPIVersion) {
-        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
-        return false;
-    }
-
-    // Negotiate the interface version by requesting most recent known to the platform
-    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
-    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
-        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
-              "requested version %u",
-              versionToUse);
-        return false;
-    }
-
-    // Add and remove versions below as needed
-    bool useAngle = false;
-    switch (versionToUse) {
-        case 2: {
-            ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
-            void* rulesHandle = nullptr;
-            int rulesVersion = 0;
-            void* systemInfoHandle = nullptr;
-
-            // Get the symbols for the feature-support-utility library:
-#define GET_SYMBOL(symbol)                                                 \
-    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \
-    if (!symbol) {                                                         \
-        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
-        break;                                                             \
-    }
-            GET_SYMBOL(ANGLEAndroidParseRulesString);
-            GET_SYMBOL(ANGLEGetSystemInfo);
-            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
-            GET_SYMBOL(ANGLEShouldBeUsedForApplication);
-            GET_SYMBOL(ANGLEFreeRulesHandle);
-            GET_SYMBOL(ANGLEFreeSystemInfoHandle);
-
-            // Parse the rules, obtain the SystemInfo, and evaluate the
-            // application against the rules:
-            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
-                ALOGW("ANGLE feature-support library cannot parse rules file");
-                break;
-            }
-            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
-                break;
-            }
-            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
-                                                  systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
-                break;
-            }
-            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
-                                                         systemInfoHandle, mAngleAppName.c_str());
-            (ANGLEFreeRulesHandle)(rulesHandle);
-            (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
-        } break;
-
-        default:
-            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
-    }
-
-    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
-    return useAngle;
-}
-
 bool GraphicsEnv::shouldUseAngle(std::string appName) {
     if (appName != mAngleAppName) {
         // Make sure we are checking the app we were init'ed for
@@ -444,31 +370,20 @@
     const char* ANGLE_PREFER_ANGLE = "angle";
     const char* ANGLE_PREFER_NATIVE = "native";
 
+    mUseAngle = NO;
     if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
         ALOGV("User set \"Developer Options\" to force the use of ANGLE");
         mUseAngle = YES;
     } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
         ALOGV("User set \"Developer Options\" to force the use of Native");
-        mUseAngle = NO;
     } else {
-        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
-        // load ANGLE and call the updatable opt-in/out logic:
-        void* featureSo = loadLibrary("feature_support");
-        if (featureSo) {
-            ALOGV("loaded ANGLE's opt-in/out logic from namespace");
-            mUseAngle = checkAngleRules(featureSo) ? YES : NO;
-            dlclose(featureSo);
-            featureSo = nullptr;
-        } else {
-            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
-        }
+        ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
     }
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
                                const std::string developerOptIn,
-                               const std::vector<std::string> eglFeatures, const int rulesFd,
-                               const long rulesOffset, const long rulesLength) {
+                               const std::vector<std::string> eglFeatures) {
     if (mUseAngle != UNKNOWN) {
         // We've already figured out an answer for this app, so just return.
         ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -485,22 +400,6 @@
     ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
     mAngleDeveloperOptIn = developerOptIn;
 
-    lseek(rulesFd, rulesOffset, SEEK_SET);
-    mRulesBuffer = std::vector<char>(rulesLength + 1);
-    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
-    if (numBytesRead < 0) {
-        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
-        numBytesRead = 0;
-    } else if (numBytesRead == 0) {
-        ALOGW("Empty rules file");
-    }
-    if (numBytesRead != rulesLength) {
-        ALOGW("Did not read all of the necessary bytes from the rules file."
-              "expected: %ld, got: %zd",
-              rulesLength, numBytesRead);
-    }
-    mRulesBuffer[numBytesRead] = '\0';
-
     // Update the current status of whether we should use ANGLE or not
     updateUseAngle();
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 9aba69f..5b513d2 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <chrono>
 #include <string>
 #include <vector>
 
@@ -52,7 +53,7 @@
 };
 
 /*
- * class for transporting gpu app stats from GpuService to authorized recipents.
+ * class for transporting gpu app stats from GpuService to authorized recipients.
  * This class is intended to be a data container.
  */
 class GpuStatsAppInfo : public Parcelable {
@@ -72,6 +73,9 @@
     bool cpuVulkanInUse = false;
     bool falsePrerotation = false;
     bool gles1InUse = false;
+    bool angleInUse = false;
+
+    std::chrono::time_point<std::chrono::system_clock> lastAccessTime;
 };
 
 /*
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 900fc49..56d1139 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,8 +97,7 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
-                      const std::vector<std::string> eglFeatures, const int rulesFd,
-                      const long rulesOffset, const long rulesLength);
+                      const std::vector<std::string> eglFeatures);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
     // Get the app name for ANGLE debug message.
@@ -129,8 +128,6 @@
 
     // Load requested ANGLE library.
     void* loadLibrary(std::string name);
-    // Check ANGLE support with the rules.
-    bool checkAngleRules(void* so);
     // Update whether ANGLE should be used.
     void updateUseAngle();
     // Link updatable driver namespace with llndk and vndk-sp libs.
@@ -159,8 +156,6 @@
     std::string mAngleDeveloperOptIn;
     // ANGLE EGL features;
     std::vector<std::string> mAngleEglFeatures;
-    // ANGLE rules.
-    std::vector<char> mRulesBuffer;
     // Use ANGLE flag.
     UseAngle mUseAngle = UNKNOWN;
     // Vulkan debug layers libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3190038..fa853cb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -78,11 +78,10 @@
     ],
 
     aidl: {
-        export_aidl_headers: true
-    }
+        export_aidl_headers: true,
+    },
 }
 
-
 cc_library_shared {
     name: "libgui",
     vendor_available: true,
@@ -192,10 +191,7 @@
         "libpdx_headers",
     ],
 
-    pgo: {
-        sampling: true,
-        profile_file: "libgui/libgui.profdata",
-    },
+    afdo: true,
 
     lto: {
         thin: true,
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56a9683..5b59c59 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -117,16 +117,11 @@
     if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
 }
 
-void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) {
-    Mutex::Autolock lock(mMutex);
-    mBLASTBufferQueue = blastbufferqueue;
-}
-
 void BLASTBufferItemConsumer::onSidebandStreamChanged() {
-    Mutex::Autolock lock(mMutex);
-    if (mBLASTBufferQueue != nullptr) {
+    sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+    if (bbq != nullptr) {
         sp<NativeHandle> stream = getSidebandStream();
-        mBLASTBufferQueue->setSidebandStream(stream);
+        bbq->setSidebandStream(stream);
     }
 }
 
@@ -147,7 +142,7 @@
     mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                       GraphicBuffer::USAGE_HW_COMPOSER |
                                                               GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false);
+                                                      1, false, this);
     static int32_t id = 0;
     mName = name + "#" + std::to_string(id);
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
@@ -158,7 +153,6 @@
     mBufferItemConsumer->setBufferFreedListener(this);
     mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
     mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
-    mBufferItemConsumer->setBlastBufferQueue(this);
 
     ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
@@ -177,7 +171,6 @@
 }
 
 BLASTBufferQueue::~BLASTBufferQueue() {
-    mBufferItemConsumer->setBlastBufferQueue(nullptr);
     if (mPendingTransactions.empty()) {
         return;
     }
@@ -630,7 +623,10 @@
 
 class BBQSurface : public Surface {
 private:
+    std::mutex mMutex;
     sp<BLASTBufferQueue> mBbq;
+    bool mDestroyed = false;
+
 public:
     BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
                const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
@@ -650,6 +646,10 @@
 
     status_t setFrameRate(float frameRate, int8_t compatibility,
                           int8_t changeFrameRateStrategy) override {
+        std::unique_lock _lock{mMutex};
+        if (mDestroyed) {
+            return DEAD_OBJECT;
+        }
         if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
                                "BBQSurface::setFrameRate")) {
             return BAD_VALUE;
@@ -658,8 +658,20 @@
     }
 
     status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+        std::unique_lock _lock{mMutex};
+        if (mDestroyed) {
+            return DEAD_OBJECT;
+        }
         return mBbq->setFrameTimelineInfo(frameTimelineInfo);
     }
+
+    void destroy() override {
+        Surface::destroy();
+
+        std::unique_lock _lock{mMutex};
+        mDestroyed = true;
+        mBbq = nullptr;
+    }
 };
 
 // TODO: Can we coalesce this with frame updates? Need to confirm
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2edb4e4..353a91d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2622,4 +2622,14 @@
     return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
 }
 
+sp<IBinder> Surface::getSurfaceControlHandle() const {
+    Mutex::Autolock lock(mMutex);
+    return mSurfaceControlHandle;
+}
+
+void Surface::destroy() {
+    Mutex::Autolock lock(mMutex);
+    mSurfaceControlHandle = nullptr;
+}
+
 }; // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index ea9b1c6..bc35792 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -38,11 +38,11 @@
 class BLASTBufferItemConsumer : public BufferItemConsumer {
 public:
     BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp)
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
           : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+            mBLASTBufferQueue(std::move(bbq)),
             mCurrentlyConnected(false),
-            mPreviouslyConnected(false),
-            mBLASTBufferQueue(nullptr) {}
+            mPreviouslyConnected(false) {}
 
     void onDisconnect() override;
     void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -59,6 +59,8 @@
     void onSidebandStreamChanged() override REQUIRES(mMutex);
 
 private:
+    const wp<BLASTBufferQueue> mBLASTBufferQueue;
+
     uint64_t mCurrentFrameNumber = 0;
 
     Mutex mMutex;
@@ -66,7 +68,6 @@
     std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex);
     bool mCurrentlyConnected GUARDED_BY(mMutex);
     bool mPreviouslyConnected GUARDED_BY(mMutex);
-    BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex);
 };
 
 class BLASTBufferQueue
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 7e4143b..e540351 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -99,7 +99,7 @@
      */
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
 
-    sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+    sp<IBinder> getSurfaceControlHandle() const;
 
     /* convenience function to check that the given surface is non NULL as
      * well as its IGraphicBufferProducer */
@@ -333,6 +333,7 @@
     virtual int connect(
             int api, bool reportBufferRemoval,
             const sp<SurfaceListener>& sListener);
+    virtual void destroy();
 
     // When client connects to Surface with reportBufferRemoval set to true, any buffers removed
     // from this Surface will be collected and returned here. Once this method returns, these
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index b9ec14a..e5d2684 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <stdarg.h>
+
 #include <cutils/trace.h>
 #include <utils/Trace.h>
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 3c59f11..94023e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -424,14 +424,28 @@
     return fenceFd;
 }
 
-bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
+void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) {
+    if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
+        ATRACE_NAME("SkiaGLRenderEngine::waitFence");
+        sync_wait(fenceFd.get(), -1);
+    }
+}
+
+bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
     if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
         !gl::GLExtensions::getInstance().hasWaitSync()) {
         return false;
     }
 
+    // Duplicate the fence for passing to eglCreateSyncKHR.
+    base::unique_fd fenceDup(dup(fenceFd.get()));
+    if (fenceDup.get() < 0) {
+        ALOGE("failed to create duplicate fence fd: %d", fenceDup.get());
+        return false;
+    }
+
     // release the fd and transfer the ownership to EGLSync
-    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceDup.release(), EGL_NONE};
     EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
     if (sync == EGL_NO_SYNC_KHR) {
         ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
@@ -726,14 +740,6 @@
         return NO_ERROR;
     }
 
-    if (bufferFence.get() >= 0) {
-        // Duplicate the fence for passing to waitFence.
-        base::unique_fd bufferFenceDup(dup(bufferFence.get()));
-        if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
-            ATRACE_NAME("Waiting before draw");
-            sync_wait(bufferFence.get(), -1);
-        }
-    }
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
         return BAD_VALUE;
@@ -758,6 +764,9 @@
                                                                true, mTextureCleanupMgr);
     }
 
+    // wait on the buffer to be ready to use prior to using it
+    waitFence(bufferFence);
+
     const ui::Dataspace dstDataspace =
             mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
     sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
@@ -1014,6 +1023,12 @@
                                                       false, mTextureCleanupMgr);
             }
 
+            // if the layer's buffer has a fence, then we must must respect the fence prior to using
+            // the buffer.
+            if (layer->source.buffer.fence != nullptr) {
+                waitFence(layer->source.buffer.fence->get());
+            }
+
             // isOpaque means we need to ignore the alpha in the image,
             // replacing it with the alpha specified by the LayerSettings. See
             // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index a852bbc..238ad8f 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -99,7 +99,10 @@
     inline GrDirectContext* getActiveGrContext() const;
 
     base::unique_fd flush();
-    bool waitFence(base::unique_fd fenceFd);
+    // waitFence attempts to wait in the GPU, and if unable to waits on the CPU instead.
+    void waitFence(base::borrowed_fd fenceFd);
+    bool waitGpuFence(base::borrowed_fd fenceFd);
+
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index eed58c5..506e308 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -54,7 +54,7 @@
     target: {
         windows: {
             enabled: true,
-        }
+        },
     },
 
     defaults: [
@@ -225,6 +225,8 @@
         "libui_headers",
     ],
     min_sdk_version: "29",
+
+    afdo: true,
 }
 
 cc_library_headers {
@@ -266,6 +268,6 @@
         "Rect.cpp",
         "Region.cpp",
         "PixelFormat.cpp",
-        "Transform.cpp"
+        "Transform.cpp",
     ],
 }
diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md
index bfc44db..f6a8f14 100644
--- a/opengl/libs/EGL/GLES_layers.md
+++ b/opengl/libs/EGL/GLES_layers.md
@@ -251,7 +251,7 @@
    - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system:
      ```cpp
      std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
-     if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos)
+     if (display_extensions.find("EGL_ANDROID_GLES_layers") != std::string::npos)
      {
         // Layers are supported!
      }
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index e6fb2c3..d11631b 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -7,7 +7,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libaudiomanager",
 
     srcs: [
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 1e8eb1e..178bc29 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -40,6 +40,7 @@
     bool chargerAcOnline;
     bool chargerUsbOnline;
     bool chargerWirelessOnline;
+    bool chargerDockOnline;
     int maxChargingCurrent;
     int maxChargingVoltage;
     int batteryStatus;
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index ac300d0..0ff65bf 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,2 +1,6 @@
 chrisforbes@google.com
 lpy@google.com
+alecmouri@google.com
+lfy@google.com
+paulthomson@google.com
+pbaiget@google.com
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
index 830e53d..24087ac 100644
--- a/services/gpuservice/gpumem/Android.bp
+++ b/services/gpuservice/gpumem/Android.bp
@@ -28,7 +28,7 @@
     ],
     shared_libs: [
         "libbase",
-        "libbpf",
+        "libbpf_bcc",
         "libbpf_android",
         "libcutils",
         "liblog",
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 3aa862f..dd3cc3b 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -22,7 +22,7 @@
 
 #include <android-base/stringprintf.h>
 #include <libbpf.h>
-#include <libbpf_android.h>
+#include <bpf/WaitForProgsLoaded.h>
 #include <log/log.h>
 #include <unistd.h>
 #include <utils/Timers.h>
diff --git a/services/gpuservice/gpuservice.rc b/services/gpuservice/gpuservice.rc
index 65a5c27..0da8bd3 100644
--- a/services/gpuservice/gpuservice.rc
+++ b/services/gpuservice/gpuservice.rc
@@ -1,4 +1,4 @@
 service gpu /system/bin/gpuservice
     class core
     user gpu_service
-    group graphics
+    group graphics readtracefs
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 220952d..d033453 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -84,6 +84,38 @@
     }
 }
 
+void GpuStats::purgeOldDriverStats() {
+    ALOG_ASSERT(mAppStats.size() == MAX_NUM_APP_RECORDS);
+
+    struct GpuStatsApp {
+        // Key is <app package name>+<driver version code>.
+        const std::string *appStatsKey = nullptr;
+        const std::chrono::time_point<std::chrono::system_clock> *lastAccessTime = nullptr;
+    };
+    std::vector<GpuStatsApp> gpuStatsApps(MAX_NUM_APP_RECORDS);
+
+    // Create a list of pointers to package names and their last access times.
+    int index = 0;
+    for (const auto & [appStatsKey, gpuStatsAppInfo] : mAppStats) {
+        GpuStatsApp &gpuStatsApp = gpuStatsApps[index];
+        gpuStatsApp.appStatsKey = &appStatsKey;
+        gpuStatsApp.lastAccessTime = &gpuStatsAppInfo.lastAccessTime;
+        ++index;
+    }
+
+    // Sort the list with the oldest access times at the front.
+    std::sort(gpuStatsApps.begin(), gpuStatsApps.end(), [](GpuStatsApp a, GpuStatsApp b) -> bool {
+        return *a.lastAccessTime < *b.lastAccessTime;
+    });
+
+    // Remove the oldest packages from mAppStats to make room for new apps.
+    for (int i = 0; i < APP_RECORD_HEADROOM; ++i) {
+        mAppStats.erase(*gpuStatsApps[i].appStatsKey);
+        gpuStatsApps[i].appStatsKey = nullptr;
+        gpuStatsApps[i].lastAccessTime = nullptr;
+    }
+}
+
 void GpuStats::insertDriverStats(const std::string& driverPackageName,
                                  const std::string& driverVersionName, uint64_t driverVersionCode,
                                  int64_t driverBuildTime, const std::string& appPackageName,
@@ -123,19 +155,22 @@
     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
     if (!mAppStats.count(appStatsKey)) {
         if (mAppStats.size() >= MAX_NUM_APP_RECORDS) {
-            ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats.");
-            return;
+            ALOGV("GpuStatsAppInfo has reached maximum size. Removing old stats to make room.");
+            purgeOldDriverStats();
         }
 
         GpuStatsAppInfo appInfo;
         addLoadingTime(driver, driverLoadingTime, &appInfo);
         appInfo.appPackageName = appPackageName;
         appInfo.driverVersionCode = driverVersionCode;
+        appInfo.angleInUse = driverPackageName == "angle";
+        appInfo.lastAccessTime = std::chrono::system_clock::now();
         mAppStats.insert({appStatsKey, appInfo});
-        return;
+    } else {
+        mAppStats[appStatsKey].angleInUse = driverPackageName == "angle";
+        addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
+        mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
     }
-
-    addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
 }
 
 void GpuStats::insertTargetStats(const std::string& appPackageName,
@@ -311,7 +346,8 @@
                                               angleDriverBytes.length()),
                     ele.second.cpuVulkanInUse,
                     ele.second.falsePrerotation,
-                    ele.second.gles1InUse);
+                    ele.second.gles1InUse,
+                    ele.second.angleInUse);
         }
     }
 
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 55f0da1..2aba651 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -46,6 +46,11 @@
 
     // This limits the worst case number of loading times tracked.
     static const size_t MAX_NUM_LOADING_TIMES = 50;
+    // Below limits the memory usage of GpuStats to be less than 10KB. This is
+    // the preferred number for statsd while maintaining nice data quality.
+    static const size_t MAX_NUM_APP_RECORDS = 100;
+    // The number of apps to remove when mAppStats fills up.
+    static const size_t APP_RECORD_HEADROOM = 10;
 
 private:
     // Friend class for testing.
@@ -55,6 +60,10 @@
     static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
                                                                  AStatsEventList* data,
                                                                  void* cookie);
+
+    // Remove old packages from mAppStats.
+    void purgeOldDriverStats();
+
     // Pull global into into global atom.
     AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data);
     // Pull app into into app atom.
@@ -68,9 +77,6 @@
     // Registers statsd callbacks if they have not already been registered
     void registerStatsdCallbacksIfNeeded();
 
-    // Below limits the memory usage of GpuStats to be less than 10KB. This is
-    // the preferred number for statsd while maintaining nice data quality.
-    static const size_t MAX_NUM_APP_RECORDS = 100;
     // GpuStats access should be guarded by mLock.
     std::mutex mLock;
     // True if statsd callbacks have been registered.
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 6d87c45..5b69f96 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -34,7 +34,7 @@
     ],
     shared_libs: [
         "libbase",
-        "libbpf",
+        "libbpf_bcc",
         "libbpf_android",
         "libcutils",
         "libgfxstats",
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 37ebeae..20c8ccf 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "gpuservice_unittest"
 
+#include <unistd.h>
 #include <cutils/properties.h>
 #include <gmock/gmock.h>
 #include <gpustats/GpuStats.h>
@@ -221,6 +222,51 @@
     EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
 }
 
+// Verify we always have the most recently used apps in mAppStats, even when we fill it.
+TEST_F(GpuStatsTest, canInsertMoreThanMaxNumAppRecords) {
+    constexpr int kNumExtraApps = 15;
+    static_assert(kNumExtraApps > GpuStats::APP_RECORD_HEADROOM);
+
+    // Insert stats for GpuStats::MAX_NUM_APP_RECORDS so we fill it up.
+    for (int i = 0; i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; ++i) {
+        std::stringstream nameStream;
+        nameStream << "testapp" << "_" << i;
+        std::string fullPkgName = nameStream.str();
+
+        mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                     BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME,
+                                     fullPkgName, VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                     DRIVER_LOADING_TIME_1);
+        mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+                                     GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+        mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+                                     GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+        mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+                                     GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+        EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
+        EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+        EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+        EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+    }
+
+    // mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up.
+    int numPurges = kNumExtraApps / GpuStats::APP_RECORD_HEADROOM;
+    numPurges += (kNumExtraApps % GpuStats::APP_RECORD_HEADROOM) == 0 ? 0 : 1;
+
+    // Verify the remaining apps are present.
+    for (int i = numPurges * GpuStats::APP_RECORD_HEADROOM;
+         i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps;
+         ++i) {
+        std::stringstream nameStream;
+        // Add a newline to search for the exact package name.
+        nameStream << "testapp" << "_" << i << "\n";
+        std::string fullPkgName = nameStream.str();
+
+        EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
+    }
+}
+
 TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
     mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
                                  BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
new file mode 100644
index 0000000..e789052
--- /dev/null
+++ b/services/gpuservice/vts/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 653544
+paulthomson@google.com
+pbaiget@google.com
+lfy@google.com
+chrisforbes@google.com
+lpy@google.com
+alecmouri@google.com
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 33b3e1e..71b0f5f 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -287,16 +287,16 @@
 
 // --- NotifyPointerCaptureChangedArgs ---
 
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
-                                                                 bool enabled)
-      : NotifyArgs(id, eventTime), enabled(enabled) {}
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+      : NotifyArgs(id, eventTime), request(request) {}
 
 NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
         const NotifyPointerCaptureChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+      : NotifyArgs(other.id, other.eventTime), request(other.request) {}
 
 bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+    return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
 }
 
 void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 9cc777d..d34482f 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -67,6 +67,9 @@
     if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
         result += "EXTERNAL_STYLUS_PRESENCE | ";
     }
+    if (changes & CHANGE_POINTER_CAPTURE) {
+        result += "POINTER_CAPTURE | ";
+    }
     if (changes & CHANGE_ENABLED_STATE) {
         result += "ENABLED_STATE | ";
     }
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index bc77b8a..aa8cc30 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -113,7 +113,7 @@
 
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
 
-    void setPointerCapture(bool enabled) override {}
+    void setPointerCapture(const PointerCaptureRequest&) override {}
 
     void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 881024f..5c3747e 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -119,15 +119,15 @@
 // PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
 // for all entries.
 PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
-                                                       bool hasPointerCapture)
+                                                       const PointerCaptureRequest& request)
       : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
-        pointerCaptureEnabled(hasPointerCapture) {}
+        pointerCaptureRequest(request) {}
 
 PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
 
 std::string PointerCaptureChangedEntry::getDescription() const {
     return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
-                        pointerCaptureEnabled ? "true" : "false");
+                        pointerCaptureRequest.enable ? "true" : "false");
 }
 
 // --- DragEntry ---
@@ -324,8 +324,7 @@
         keyEntry(nullptr),
         userActivityEventType(0),
         seq(0),
-        handled(false),
-        enabled(false) {}
+        handled(false) {}
 
 CommandEntry::~CommandEntry() {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ebbd8e9..6f1dfad 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -104,9 +104,9 @@
 };
 
 struct PointerCaptureChangedEntry : EventEntry {
-    bool pointerCaptureEnabled;
+    const PointerCaptureRequest pointerCaptureRequest;
 
-    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
     std::string getDescription() const override;
 
     ~PointerCaptureChangedEntry() override;
@@ -284,7 +284,7 @@
     sp<IBinder> oldToken;
     sp<IBinder> newToken;
     std::string obscuringPackage;
-    bool enabled;
+    PointerCaptureRequest pointerCaptureRequest;
     int32_t pid;
     nsecs_t consumeTime; // time when the event was consumed by InputConsumer
     int32_t displayId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c2a2794..6e9430a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -523,7 +523,6 @@
         mInTouchMode(true),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
-        mFocusedWindowRequestedPointerCapture(false),
         mWindowTokenWithPointerCapture(nullptr),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator),
@@ -1311,36 +1310,51 @@
 void InputDispatcher::dispatchPointerCaptureChangedLocked(
         nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
         DropReason& dropReason) {
+    dropReason = DropReason::NOT_DROPPED;
+
     const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
-    if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
-        LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
-    }
-    if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
-        // Pointer capture was already forcefully disabled because of focus change.
-        dropReason = DropReason::NOT_DROPPED;
-        return;
-    }
-
-    // Set drop reason for early returns
-    dropReason = DropReason::NO_POINTER_CAPTURE;
-
     sp<IBinder> token;
-    if (entry->pointerCaptureEnabled) {
-        // Enable Pointer Capture
-        if (!mFocusedWindowRequestedPointerCapture) {
+
+    if (entry->pointerCaptureRequest.enable) {
+        // Enable Pointer Capture.
+        if (haveWindowWithPointerCapture &&
+            (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
+            LOG_ALWAYS_FATAL("This request to enable Pointer Capture has already been dispatched "
+                             "to the window.");
+        }
+        if (!mCurrentPointerCaptureRequest.enable) {
             // This can happen if a window requests capture and immediately releases capture.
             ALOGW("No window requested Pointer Capture.");
+            dropReason = DropReason::NO_POINTER_CAPTURE;
             return;
         }
+        if (entry->pointerCaptureRequest.seq != mCurrentPointerCaptureRequest.seq) {
+            ALOGI("Skipping dispatch of Pointer Capture being enabled: sequence number mismatch.");
+            return;
+        }
+
         token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
         LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
         mWindowTokenWithPointerCapture = token;
     } else {
-        // Disable Pointer Capture
+        // Disable Pointer Capture.
+        // We do not check if the sequence number matches for requests to disable Pointer Capture
+        // for two reasons:
+        //  1. Pointer Capture can be disabled by a focus change, which means we can get two entries
+        //     to disable capture with the same sequence number: one generated by
+        //     disablePointerCaptureForcedLocked() and another as an acknowledgement of Pointer
+        //     Capture being disabled in InputReader.
+        //  2. We respect any request to disable Pointer Capture generated by InputReader, since the
+        //     actual Pointer Capture state that affects events being generated by input devices is
+        //     in InputReader.
+        if (!haveWindowWithPointerCapture) {
+            // Pointer capture was already forcefully disabled because of focus change.
+            dropReason = DropReason::NOT_DROPPED;
+            return;
+        }
         token = mWindowTokenWithPointerCapture;
         mWindowTokenWithPointerCapture = nullptr;
-        if (mFocusedWindowRequestedPointerCapture) {
-            mFocusedWindowRequestedPointerCapture = false;
+        if (mCurrentPointerCaptureRequest.enable) {
             setPointerCaptureLocked(false);
         }
     }
@@ -1349,8 +1363,7 @@
     if (channel == nullptr) {
         // Window has gone away, clean up Pointer Capture state.
         mWindowTokenWithPointerCapture = nullptr;
-        if (mFocusedWindowRequestedPointerCapture) {
-            mFocusedWindowRequestedPointerCapture = false;
+        if (mCurrentPointerCaptureRequest.enable) {
             setPointerCaptureLocked(false);
         }
         return;
@@ -3185,7 +3198,7 @@
                         static_cast<const PointerCaptureChangedEntry&>(eventEntry);
                 status = connection->inputPublisher
                                  .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
-                                                      captureEntry.pointerCaptureEnabled);
+                                                      captureEntry.pointerCaptureRequest.enable);
                 break;
             }
 
@@ -3990,14 +4003,14 @@
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-          args->enabled ? "true" : "false");
+          args->request.enable ? "true" : "false");
 #endif
 
     bool needWake;
     { // acquire lock
         std::scoped_lock _l(mLock);
         auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
-                                                                  args->enabled);
+                                                                  args->request);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
 
@@ -4941,8 +4954,8 @@
 std::string InputDispatcher::dumpPointerCaptureStateLocked() {
     std::string dump;
 
-    dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
-                         toString(mFocusedWindowRequestedPointerCapture));
+    dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
+                         toString(mCurrentPointerCaptureRequest.enable));
 
     std::string windowName = "None";
     if (mWindowTokenWithPointerCapture) {
@@ -4951,7 +4964,7 @@
         windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
                                          : "token has capture without window";
     }
-    dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+    dump += StringPrintf(INDENT "Current Window with Pointer Capture: %s\n", windowName.c_str());
 
     return dump;
 }
@@ -5419,14 +5432,13 @@
             return;
         }
 
-        if (enabled == mFocusedWindowRequestedPointerCapture) {
+        if (enabled == mCurrentPointerCaptureRequest.enable) {
             ALOGW("Ignoring request to %s Pointer Capture: "
                   "window has %s requested pointer capture.",
                   enabled ? "enable" : "disable", enabled ? "already" : "not");
             return;
         }
 
-        mFocusedWindowRequestedPointerCapture = enabled;
         setPointerCaptureLocked(enabled);
     } // release lock
 
@@ -6185,14 +6197,13 @@
 }
 
 void InputDispatcher::disablePointerCaptureForcedLocked() {
-    if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+    if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
         return;
     }
 
     ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
 
-    if (mFocusedWindowRequestedPointerCapture) {
-        mFocusedWindowRequestedPointerCapture = false;
+    if (mCurrentPointerCaptureRequest.enable) {
         setPointerCaptureLocked(false);
     }
 
@@ -6209,14 +6220,16 @@
     }
 
     auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
-                                                              false /* hasCapture */);
+                                                              mCurrentPointerCaptureRequest);
     mInboundQueue.push_front(std::move(entry));
 }
 
 void InputDispatcher::setPointerCaptureLocked(bool enabled) {
+    mCurrentPointerCaptureRequest.enable = enabled;
+    mCurrentPointerCaptureRequest.seq++;
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doSetPointerCaptureLockedInterruptible);
-    commandEntry->enabled = enabled;
+    commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest;
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -6224,7 +6237,7 @@
         android::inputdispatcher::CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->setPointerCapture(commandEntry->enabled);
+    mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest);
 
     mLock.lock();
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9edf41c..30652c6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -357,10 +357,12 @@
 
     // Keeps track of the focused window per display and determines focus changes.
     FocusResolver mFocusResolver GUARDED_BY(mLock);
-    // Whether the focused window on the focused display has requested Pointer Capture.
-    // The state of this variable should always be in sync with the state of Pointer Capture in the
-    // policy, which is updated through setPointerCaptureLocked(enabled).
-    bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+    // The enabled state of this request is true iff the focused window on the focused display has
+    // requested Pointer Capture. This request also contains the sequence number associated with the
+    // current request. The state of this variable should always be in sync with the state of
+    // Pointer Capture in the policy, and is only updated through setPointerCaptureLocked(request).
+    PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
 
     // The window token that has Pointer Capture.
     // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
@@ -370,7 +372,7 @@
     void disablePointerCaptureForcedLocked() REQUIRES(mLock);
 
     // Set the Pointer Capture state in the Policy.
-    void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+    void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
 
     // Dispatcher state at time of last ANR.
     std::string mLastAnrState GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 219f45a..fd591e0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -156,7 +156,7 @@
      *
      * InputDispatcher is solely responsible for updating the Pointer Capture state.
      */
-    virtual void setPointerCapture(bool enabled) = 0;
+    virtual void setPointerCapture(const PointerCaptureRequest&) = 0;
 
     /* Notifies the policy that the drag window has moved over to another window */
     virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
index 8da699d..0b44187 100644
--- a/services/inputflinger/docs/pointer_capture.md
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -17,6 +17,8 @@
 
 `InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
 
+We use a sequence number to synchronize different requests to enable Pointer Capture between InputReader and InputDispatcher.
+
 ### Enabling Pointer Capture
 
 There are four key steps that take place when Pointer Capture is enabled:
@@ -40,5 +42,5 @@
 
 `InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
 
-- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mCurrentPointerCaptureRequest`: The sequence number of the current Pointer Capture request. This request is enabled iff the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
 - `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 4b7d26d..fe74214 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -211,11 +211,12 @@
 
 /* Describes a change in the state of Pointer Capture. */
 struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
-    bool enabled;
+    // The sequence number of the Pointer Capture request, if enabled.
+    PointerCaptureRequest request;
 
     inline NotifyPointerCaptureChangedArgs() {}
 
-    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
 
     NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 7fdbbfd..3c8ac1c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -279,29 +279,30 @@
     // True to show the location of touches on the touch screen as spots.
     bool showTouches;
 
-    // True if pointer capture is enabled.
-    bool pointerCapture;
+    // The latest request to enable or disable Pointer Capture.
+    PointerCaptureRequest pointerCaptureRequest;
 
     // The set of currently disabled input devices.
     std::set<int32_t> disabledDevices;
 
-    InputReaderConfiguration() :
-            virtualKeyQuietTime(0),
+    InputReaderConfiguration()
+          : virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGesturesEnabled(true),
-            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
-            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
-            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapSlop(10.0f), // 10 pixels
+            pointerGestureQuietInterval(100 * 1000000LL),            // 100 ms
+            pointerGestureDragMinSwitchSpeed(50),                    // 50 pixels per second
+            pointerGestureTapInterval(150 * 1000000LL),              // 150 ms
+            pointerGestureTapDragInterval(150 * 1000000LL),          // 150 ms
+            pointerGestureTapSlop(10.0f),                            // 10 pixels
             pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
-            pointerGestureMultitouchMinDistance(15), // 15 pixels
-            pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+            pointerGestureMultitouchMinDistance(15),                 // 15 pixels
+            pointerGestureSwipeTransitionAngleCosine(0.2588f),       // cosine of 75 degrees
             pointerGestureSwipeMaxWidthRatio(0.25f),
             pointerGestureMovementSpeedRatio(0.8f),
             pointerGestureZoomSpeedRatio(0.3f),
-            showTouches(false), pointerCapture(false) { }
+            showTouches(false),
+            pointerCaptureRequest() {}
 
     static std::string changesToString(uint32_t changes);
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 10c04f6..5120860 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -367,9 +367,15 @@
     }
 
     if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
-        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
-                                                   mConfig.pointerCapture);
-        mQueuedListener->notifyPointerCaptureChanged(&args);
+        if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
+            ALOGV("Skipping notifying pointer capture changes: "
+                  "There was no change in the pointer capture state.");
+        } else {
+            mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
+            const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+                                                       mCurrentPointerCaptureRequest);
+            mQueuedListener->notifyPointerCaptureChanged(&args);
+        }
     }
 }
 
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index a00c5af..e44aa0f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -230,6 +230,8 @@
     uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
     void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
 
+    PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
+
     // state queries
     typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 437902a..2ac41b1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -154,9 +154,9 @@
         mHWheelScale = 1.0f;
     }
 
-    if ((!changes && config->pointerCapture) ||
+    if ((!changes && config->pointerCaptureRequest.enable) ||
         (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
-        if (config->pointerCapture) {
+        if (config->pointerCaptureRequest.enable) {
             if (mParameters.mode == Parameters::MODE_POINTER) {
                 mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
                 mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f457315..ae89ca1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -603,7 +603,7 @@
 
     // Determine device mode.
     if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
-        mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
+        mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
         mSource = AINPUT_SOURCE_MOUSE;
         mDeviceMode = DeviceMode::POINTER;
         if (hasStylus()) {
@@ -776,11 +776,12 @@
     // preserve the cursor position.
     if (mDeviceMode == DeviceMode::POINTER ||
         (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
-        (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
+        (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+         mConfig.pointerCaptureRequest.enable)) {
         if (mPointerController == nullptr) {
             mPointerController = getContext()->getPointerController(getDeviceId());
         }
-        if (mConfig.pointerCapture) {
+        if (mConfig.pointerCaptureRequest.enable) {
             mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
         }
     } else {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dff0752..7d4c638 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -239,19 +239,22 @@
         mConfig.keyRepeatDelay = delay;
     }
 
-    void waitForSetPointerCapture(bool enabled) {
+    PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
         std::unique_lock lock(mLock);
         base::ScopedLockAssertion assumeLocked(mLock);
 
         if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
                                                       [this, enabled]() REQUIRES(mLock) {
-                                                          return mPointerCaptureEnabled &&
-                                                                  *mPointerCaptureEnabled ==
+                                                          return mPointerCaptureRequest->enable ==
                                                                   enabled;
                                                       })) {
-            FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+            ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled
+                          << ") to be called.";
+            return {};
         }
-        mPointerCaptureEnabled.reset();
+        auto request = *mPointerCaptureRequest;
+        mPointerCaptureRequest.reset();
+        return request;
     }
 
     void assertSetPointerCaptureNotCalled() {
@@ -259,11 +262,11 @@
         base::ScopedLockAssertion assumeLocked(mLock);
 
         if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
-            FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+            FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
                       "enabled = "
-                   << *mPointerCaptureEnabled;
+                   << std::to_string(mPointerCaptureRequest->enable);
         }
-        mPointerCaptureEnabled.reset();
+        mPointerCaptureRequest.reset();
     }
 
     void assertDropTargetEquals(const sp<IBinder>& targetToken) {
@@ -281,7 +284,8 @@
     std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
     std::condition_variable mPointerCaptureChangedCondition;
-    std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
+
+    std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
 
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
@@ -398,9 +402,9 @@
         mOnPointerDownToken = newToken;
     }
 
-    void setPointerCapture(bool enabled) override {
+    void setPointerCapture(const PointerCaptureRequest& request) override {
         std::scoped_lock lock(mLock);
-        mPointerCaptureEnabled = {enabled};
+        mPointerCaptureRequest = {request};
         mPointerCaptureChangedCondition.notify_all();
     }
 
@@ -1379,8 +1383,9 @@
     return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
 }
 
-static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
-    return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
+        const PointerCaptureRequest& request) {
+    return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
@@ -4601,16 +4606,18 @@
         mWindow->consumeFocusEvent(true);
     }
 
-    void notifyPointerCaptureChanged(bool enabled) {
-        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+    void notifyPointerCaptureChanged(const PointerCaptureRequest& request) {
+        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request);
         mDispatcher->notifyPointerCaptureChanged(&args);
     }
 
-    void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+    PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
+                                                         bool enabled) {
         mDispatcher->requestPointerCapture(window->getToken(), enabled);
-        mFakePolicy->waitForSetPointerCapture(enabled);
-        notifyPointerCaptureChanged(enabled);
+        auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+        notifyPointerCaptureChanged(request);
         window->consumeCaptureEvent(enabled);
+        return request;
     }
 };
 
@@ -4632,7 +4639,7 @@
 }
 
 TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
-    requestAndVerifyPointerCapture(mWindow, true);
+    auto request = requestAndVerifyPointerCapture(mWindow, true);
 
     setFocusedWindow(mSecondWindow);
 
@@ -4640,26 +4647,26 @@
     mWindow->consumeCaptureEvent(false);
     mWindow->consumeFocusEvent(false);
     mSecondWindow->consumeFocusEvent(true);
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
 
     // Ensure that additional state changes from InputReader are not sent to the window.
-    notifyPointerCaptureChanged(false);
-    notifyPointerCaptureChanged(true);
-    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged({});
+    notifyPointerCaptureChanged(request);
+    notifyPointerCaptureChanged({});
     mWindow->assertNoEvents();
     mSecondWindow->assertNoEvents();
     mFakePolicy->assertSetPointerCaptureNotCalled();
 }
 
 TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
-    requestAndVerifyPointerCapture(mWindow, true);
+    auto request = requestAndVerifyPointerCapture(mWindow, true);
 
     // InputReader unexpectedly disables and enables pointer capture.
-    notifyPointerCaptureChanged(false);
-    notifyPointerCaptureChanged(true);
+    notifyPointerCaptureChanged({});
+    notifyPointerCaptureChanged(request);
 
     // Ensure that Pointer Capture is disabled.
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
     mWindow->consumeCaptureEvent(false);
     mWindow->assertNoEvents();
 }
@@ -4669,24 +4676,43 @@
 
     // The first window loses focus.
     setFocusedWindow(mSecondWindow);
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
     mWindow->consumeCaptureEvent(false);
 
     // Request Pointer Capture from the second window before the notification from InputReader
     // arrives.
     mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
-    mFakePolicy->waitForSetPointerCapture(true);
+    auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
 
     // InputReader notifies Pointer Capture was disabled (because of the focus change).
-    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged({});
 
     // InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
-    notifyPointerCaptureChanged(true);
+    notifyPointerCaptureChanged(request);
 
     mSecondWindow->consumeFocusEvent(true);
     mSecondWindow->consumeCaptureEvent(true);
 }
 
+TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
+    // App repeatedly enables and disables capture.
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+    mDispatcher->requestPointerCapture(mWindow->getToken(), false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+
+    // InputReader notifies that PointerCapture has been enabled for the first request. Since the
+    // first request is now stale, this should do nothing.
+    notifyPointerCaptureChanged(firstRequest);
+    mWindow->assertNoEvents();
+
+    // InputReader notifies that the second request was enabled.
+    notifyPointerCaptureChanged(secondRequest);
+    mWindow->consumeCaptureEvent(true);
+}
+
 class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
 protected:
     constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 997cbe8..38dfe40 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -299,8 +299,9 @@
         transform = t;
     }
 
-    void setPointerCapture(bool enabled) {
-        mConfig.pointerCapture = enabled;
+    PointerCaptureRequest setPointerCapture(bool enabled) {
+        mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+        return mConfig.pointerCaptureRequest;
     }
 
     void setShowTouches(bool enabled) {
@@ -314,6 +315,8 @@
     float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
 
 private:
+    uint32_t mNextPointerCaptureSequenceNumber = 0;
+
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
                                           int32_t orientation, bool isActive,
                                           const std::string& uniqueId,
@@ -1961,24 +1964,24 @@
 TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
     NotifyPointerCaptureChangedArgs args;
 
-    mFakePolicy->setPointerCapture(true);
+    auto request = mFakePolicy->setPointerCapture(true);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+    ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+    ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
 
     mFakePolicy->setPointerCapture(false);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+    ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
 
-    // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+    // Verify that the Pointer Capture state is not updated when the configuration value
     // does not change.
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
-    mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+    mFakeListener->assertNotifyCaptureWasNotCalled();
 }
 
 class FakeVibratorInputMapper : public FakeInputMapper {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fb7de97..6a26c63 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -100,6 +100,11 @@
                                                           "to have been called."));
 }
 
+void TestInputListener::assertNotifyCaptureWasNotCalled() {
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyPointerCaptureChangedArgs>(
+            "notifyPointerCaptureChanged() should not be called."));
+}
+
 template <class NotifyArgsType>
 void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
     std::unique_lock<std::mutex> lock(mLock);
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0ffcaaa..0a1dc4b 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -55,6 +55,7 @@
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
 
     void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+    void assertNotifyCaptureWasNotCalled();
     void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
     void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr);
 
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1be5a96..e6b118b 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -78,6 +78,8 @@
         "libsensorprivacy",
         "libpermission",
     ],
+
+    afdo: true,
 }
 
 cc_binary {
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index c233bf0..ec31afe 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -326,7 +326,7 @@
     mActivationCount.clear();
     mSensorList.clear();
 
-    if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) {
+    if (connectHidlService()) {
         initializeSensorList();
 
         if (sensorHandlesChanged(previousSensorList, mSensorList)) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index eb31e92..40b1fd0 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -101,7 +101,7 @@
     // TODO (marissaw): this library is not used by surfaceflinger. This is here so
     // the library compiled in a way that is accessible to system partition when running
     // IMapper's VTS.
-    required: ["libgralloctypes"]
+    required: ["libgralloctypes"],
 }
 
 cc_defaults {
@@ -114,10 +114,7 @@
         thin: true,
     },
     whole_program_vtables: true, // Requires ThinLTO
-    pgo: {
-        sampling: true,
-        profile_file: "surfaceflinger/surfaceflinger.profdata",
-    },
+    afdo: true,
     // TODO(b/131771163): Fix broken fuzzer support with LTO.
     sanitize: {
         fuzzer: false,
@@ -247,7 +244,7 @@
         "libSurfaceFlingerProp",
     ],
 
-     logtags: ["EventLog/EventLogTags.logtags"],
+    logtags: ["EventLog/EventLogTags.logtags"],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 75dd51c..d8cead5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -820,11 +820,7 @@
 }
 
 bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
-    sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
-    if (handle == nullptr) {
-        return false;
-    }
-    sp<Layer> relative = handle->owner.promote();
+    sp<Layer> relative = fromHandle(relativeToHandle).promote();
     if (relative == nullptr) {
         return false;
     }
@@ -1610,8 +1606,7 @@
 bool Layer::reparent(const sp<IBinder>& newParentHandle) {
     sp<Layer> newParent;
     if (newParentHandle != nullptr) {
-        auto handle = static_cast<Handle*>(newParentHandle.get());
-        newParent = handle->owner.promote();
+        newParent = fromHandle(newParentHandle).promote();
         if (newParent == nullptr) {
             ALOGE("Unable to promote Layer handle");
             return false;
@@ -1986,24 +1981,10 @@
     mDrawingParent = mCurrentParent;
 }
 
-static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
-    if (weakBinderHandle == nullptr) {
-        return nullptr;
-    }
-    sp<IBinder> binderHandle = weakBinderHandle.promote();
-    if (binderHandle == nullptr) {
-        return nullptr;
-    }
-    sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
-    if (handle == nullptr) {
-        return nullptr;
-    }
-    return handle->owner;
-}
 
 void Layer::setInputInfo(const InputWindowInfo& info) {
     mDrawingState.inputInfo = info;
-    mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
+    mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
     mDrawingState.modified = true;
     mFlinger->mInputInfoChanged = true;
     setTransactionFlags(eTransactionNeeded);
@@ -2562,6 +2543,23 @@
     mFlinger->mNumClones++;
 }
 
+const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
+
+wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
+    if (handleBinder == nullptr) {
+        return nullptr;
+    }
+
+    BBinder* b = handleBinder->localBinder();
+    if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
+        return nullptr;
+    }
+
+    // We can safely cast this binder since its local and we verified its interface descriptor.
+    sp<Handle> handle = static_cast<Handle*>(handleBinder.get());
+    return handle->owner;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 59f5b0d..8905548 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -289,16 +289,17 @@
     class LayerCleaner {
         sp<SurfaceFlinger> mFlinger;
         sp<Layer> mLayer;
+        BBinder* mHandle;
 
     protected:
         ~LayerCleaner() {
             // destroy client resources
-            mFlinger->onHandleDestroyed(mLayer);
+            mFlinger->onHandleDestroyed(mHandle, mLayer);
         }
 
     public:
-        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : mFlinger(flinger), mLayer(layer) {}
+        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
+              : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
     };
 
     /*
@@ -312,11 +313,15 @@
     class Handle : public BBinder, public LayerCleaner {
     public:
         Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : LayerCleaner(flinger, layer), owner(layer) {}
+              : LayerCleaner(flinger, layer, this), owner(layer) {}
+        const String16& getInterfaceDescriptor() const override { return kDescriptor; }
 
+        static const String16 kDescriptor;
         wp<Layer> owner;
     };
 
+    static wp<Layer> fromHandle(const sp<IBinder>& handle);
+
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0334d70..73b7b63 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -911,7 +911,10 @@
 int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
-    constexpr float kThreshold = 0.1f;
+
+    // The threshold must be smaller than 0.001 in order to differentiate
+    // between the fractional pairs (e.g. 59.94 and 60).
+    constexpr float kThreshold = 0.0009f;
     const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e0b3640..64ad178 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -386,6 +386,13 @@
     }
 
     const auto modeId = *mFeatures.modeId;
+    // If the modeId is not the current mode, this means that a
+    // mode change is in progress. In that case we shouldn't dispatch an event
+    // as it will be dispatched when the current mode changes.
+    if (mRefreshRateConfigs.getCurrentRefreshRate().getModeId() != modeId) {
+        return;
+    }
+
     const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod();
 
     // If there is no change from cached mode, there is no need to dispatch an event
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index abc49bf..fabb3fe 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1960,7 +1960,10 @@
         // We received the present fence from the HWC, so we assume it successfully updated
         // the mode, hence we update SF.
         mSetActiveModePending = false;
-        ON_MAIN_THREAD(setActiveModeInternal());
+        {
+            Mutex::Autolock lock(mStateLock);
+            setActiveModeInternal();
+        }
     }
 
     if (framePending) {
@@ -3622,7 +3625,7 @@
 
         sp<Layer> layer = nullptr;
         if (s.surface) {
-            layer = fromHandleLocked(s.surface).promote();
+            layer = fromHandle(s.surface).promote();
         } else if (s.hasBufferChanges()) {
             ALOGW("Transaction with buffer, but no Layer?");
             continue;
@@ -3789,7 +3792,7 @@
                 setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
                                      postTime, permissions, listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
-            if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+            if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
                 mScheduler->recordLayerHistory(layer.get(),
                                                isAutoTimestamp ? 0 : desiredPresentTime,
                                                LayerHistory::LayerUpdateType::AnimationTX);
@@ -3944,13 +3947,11 @@
         if (what & layer_state_t::eLayerCreated) {
             layer = handleLayerCreatedLocked(s.surface);
             if (layer) {
-                // put the created layer into mLayersByLocalBinderToken.
-                mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
                 flags |= eTransactionNeeded | eTraversalNeeded;
                 mLayersAdded = true;
             }
         } else {
-            layer = fromHandleLocked(s.surface).promote();
+            layer = fromHandle(s.surface).promote();
         }
     } else {
         // The client may provide us a null handle. Treat it as if the layer was removed.
@@ -4278,7 +4279,7 @@
 
     {
         Mutex::Autolock _l(mStateLock);
-        mirrorFrom = fromHandleLocked(mirrorFromHandle).promote();
+        mirrorFrom = fromHandle(mirrorFromHandle).promote();
         if (!mirrorFrom) {
             return NAME_NOT_FOUND;
         }
@@ -4476,7 +4477,7 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
     Mutex::Autolock lock(mStateLock);
     // If a layer has a parent, we allow it to out-live it's handle
     // with the idea that the parent holds a reference and will eventually
@@ -4487,17 +4488,7 @@
         mCurrentState.layersSortedByZ.remove(layer);
     }
     markLayerPendingRemovalLocked(layer);
-
-    auto it = mLayersByLocalBinderToken.begin();
-    while (it != mLayersByLocalBinderToken.end()) {
-        if (it->second == layer) {
-            mBufferCountTracker.remove(it->first->localBinder());
-            it = mLayersByLocalBinderToken.erase(it);
-        } else {
-            it++;
-        }
-    }
-
+    mBufferCountTracker.remove(handle);
     layer.clear();
 }
 
@@ -6101,7 +6092,7 @@
     {
         Mutex::Autolock lock(mStateLock);
 
-        parent = fromHandleLocked(args.layerHandle).promote();
+        parent = fromHandle(args.layerHandle).promote();
         if (parent == nullptr || parent->isRemovedFromCurrentState()) {
             ALOGE("captureLayers called with an invalid or removed parent");
             return NAME_NOT_FOUND;
@@ -6132,7 +6123,7 @@
         reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
 
         for (const auto& handle : args.excludeHandles) {
-            sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
+            sp<Layer> excludeLayer = fromHandle(handle).promote();
             if (excludeLayer != nullptr) {
                 excludeLayers.emplace(excludeLayer);
             } else {
@@ -6628,24 +6619,8 @@
     }
 }
 
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
-    Mutex::Autolock _l(mStateLock);
-    return fromHandleLocked(handle);
-}
-
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
-    BBinder* b = nullptr;
-    if (handle) {
-        b = handle->localBinder();
-    }
-    if (b == nullptr) {
-        return nullptr;
-    }
-    auto it = mLayersByLocalBinderToken.find(b);
-    if (it != mLayersByLocalBinderToken.end()) {
-        return it->second;
-    }
-    return nullptr;
+wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
+    return Layer::fromHandle(handle);
 }
 
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
@@ -6950,7 +6925,7 @@
     sp<Layer> parent;
     bool allowAddRoot = state->addToRoot;
     if (state->initialParent != nullptr) {
-        parent = fromHandleLocked(state->initialParent.promote()).promote();
+        parent = fromHandle(state->initialParent.promote()).promote();
         if (parent == nullptr) {
             ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
             allowAddRoot = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fa19b7f..4e8e614 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -327,8 +327,7 @@
     // Returns nullptr if the handle does not point to an existing layer.
     // Otherwise, returns a weak reference so that callers off the main-thread
     // won't accidentally hold onto the last strong reference.
-    wp<Layer> fromHandle(const sp<IBinder>& handle);
-    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
+    wp<Layer> fromHandle(const sp<IBinder>& handle) const;
 
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
@@ -899,7 +898,7 @@
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
     // resources associated to this layer.
-    void onHandleDestroyed(sp<Layer>& layer);
+    void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
     void markLayerPendingRemovalLocked(const sp<Layer>& layer);
 
     // add a layer to SurfaceFlinger
@@ -1291,8 +1290,6 @@
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
     } mVirtualDisplayIdGenerators;
 
-    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
-
     // don't use a lock for these, we don't care
     int mDebugRegion = 0;
     bool mDebugDisableHWC = false;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 23ab7c8..837b2e8 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -183,12 +183,9 @@
     return NO_ERROR;
 }
 
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const {
-    const sp<const IBinder>& handle(weakHandle.promote());
-    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
-    const sp<const Layer> layer(layerHandle->owner.promote());
-    // layer could be a nullptr at this point
-    return layer;
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
+    sp<IBinder> handle = weakHandle.promote();
+    return Layer::fromHandle(handle).promote();
 }
 
 int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
@@ -203,12 +200,11 @@
     return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
 }
 
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const {
+int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
     if (handle == nullptr) {
         return -1;
     }
-    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
-    const sp<const Layer> layer(layerHandle->owner.promote());
+    const sp<const Layer> layer = Layer::fromHandle(handle).promote();
     return layer == nullptr ? -1 : getLayerId(layer);
 }
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 673f9e7..c955596 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -133,10 +133,10 @@
     void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
 
     status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const;
+    const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
     int32_t getLayerId(const sp<const Layer>& layer) const;
     int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
-    int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const;
+    int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
 
     Increment* createTraceIncrementLocked();
     void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 575e70d..39d7bd9 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -3,7 +3,7 @@
     user system
     group graphics drmrpc readproc
     capabilities SYS_NICE
-    onrestart restart zygote
+    onrestart restart --only-if-running zygote
     task_profiles HighPerformance
     socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
     socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 3423bd5..3b2bd81 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -2123,7 +2123,11 @@
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
+
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index d1cd397..fa3b260 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -965,6 +965,13 @@
     VkResult result = EnumerateDeviceExtensionProperties(physical_dev, nullptr,
                                                          &count, nullptr);
     if (result == VK_SUCCESS && count) {
+        // Work-around a race condition during Android start-up, that can result
+        // in the second call to EnumerateDeviceExtensionProperties having
+        // another extension.  That causes the second call to return
+        // VK_INCOMPLETE.  A work-around is to add 1 to "count" and ask for one
+        // more extension property.  See: http://anglebug.com/6715 and
+        // internal-to-Google b/206733351.
+        count++;
         driver_extensions_ = AllocateDriverExtensionArray(count);
         result = (driver_extensions_)
                      ? EnumerateDeviceExtensionProperties(
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d7fdab5..cf774fd 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -979,6 +979,8 @@
 void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
     VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
+    ATRACE_CALL();
+
     // Request the android-specific presentation properties via GPDP2
     VkPhysicalDeviceProperties2 properties = {
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
@@ -994,7 +996,17 @@
     presentation_properties->pNext = nullptr;
     presentation_properties->sharedImage = VK_FALSE;
 
-    GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceProperties2) {
+        // >= 1.1 driver, supports core GPDP2 entrypoint.
+        driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    } else if (driver.GetPhysicalDeviceProperties2KHR) {
+        // Old driver, but may support presentation properties
+        // if we have the GPDP2 extension. Otherwise, no presentation
+        // properties supported.
+        driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties);
+    }
 }
 
 VkResult EnumerateDeviceExtensionProperties(
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 2715587..6191063 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -537,30 +537,6 @@
     }
 }
 
-int get_min_buffer_count(ANativeWindow* window,
-                         uint32_t* out_min_buffer_count) {
-    constexpr int kExtraBuffers = 2;
-
-    int err;
-    int min_undequeued_buffers;
-    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                        &min_undequeued_buffers);
-    if (err != android::OK || min_undequeued_buffers < 0) {
-        ALOGE(
-            "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
-            "value=%d",
-            strerror(-err), err, min_undequeued_buffers);
-        if (err == android::OK) {
-            err = android::UNKNOWN_ERROR;
-        }
-        return err;
-    }
-
-    *out_min_buffer_count =
-        static_cast<uint32_t>(min_undequeued_buffers + kExtraBuffers);
-    return android::OK;
-}
-
 }  // anonymous namespace
 
 VKAPI_ATTR
@@ -675,7 +651,7 @@
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    capabilities->minImageCount = max_buffer_count == 1 ? 1 : 2;
+    capabilities->minImageCount = std::min(max_buffer_count, 3);
     capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
 
     capabilities->currentExtent =
@@ -872,13 +848,18 @@
 
     int err;
     int query_value;
-    uint32_t min_buffer_count;
     ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
-    err = get_min_buffer_count(window, &min_buffer_count);
-    if (err != android::OK) {
+    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                        &query_value);
+    if (err != android::OK || query_value < 0) {
+        ALOGE(
+            "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
+            "value=%d",
+            strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
+    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
     if (err != android::OK || query_value < 0) {
@@ -889,7 +870,7 @@
     uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
 
     std::vector<VkPresentModeKHR> present_modes;
-    if (min_buffer_count < max_buffer_count)
+    if (min_undequeued_buffers + 1 < max_buffer_count)
         present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
     present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
 
@@ -1210,27 +1191,40 @@
         }
     }
 
-    uint32_t min_buffer_count;
-    err = get_min_buffer_count(window, &min_buffer_count);
-    if (err != android::OK) {
+    int query_value;
+    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                        &query_value);
+    if (err != android::OK || query_value < 0) {
+        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
+              query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
+    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
+    const auto mailbox_num_images = std::max(3u, create_info->minImageCount);
+    const auto requested_images =
+        swap_interval ? create_info->minImageCount : mailbox_num_images;
+    uint32_t num_images = requested_images - 1 + min_undequeued_buffers;
 
-    uint32_t num_images =
-        std::max(min_buffer_count, create_info->minImageCount);
-
-    // Lower layer insists that we have at least two buffers. This is wasteful
-    // and we'd like to relax it in the shared case, but not all the pieces are
-    // in place for that to work yet. Note we only lie to the lower layer-- we
-    // don't want to give the app back a swapchain with extra images (which they
-    // can't actually use!).
-    err = native_window_set_buffer_count(window, std::max(2u, num_images));
+    // Lower layer insists that we have at least min_undequeued_buffers + 1
+    // buffers.  This is wasteful and we'd like to relax it in the shared case,
+    // but not all the pieces are in place for that to work yet.  Note we only
+    // lie to the lower layer--we don't want to give the app back a swapchain
+    // with extra images (which they can't actually use!).
+    uint32_t min_buffer_count = min_undequeued_buffers + 1;
+    err = native_window_set_buffer_count(
+        window, std::max(min_buffer_count, num_images));
     if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
+    // In shared mode the num_images must be one regardless of how many
+    // buffers were allocated for the buffer queue.
+    if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) {
+        num_images = 1;
+    }
+
     int32_t legacy_usage = 0;
     if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;