Merge "Update installd OWNERS"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index c55fc6a..a176df9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2354,7 +2354,7 @@
 
 // TODO: Consider returning error codes.
 binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
-        const std::string& profileName, bool* _aidl_return) {
+        const std::string& profileName, int* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -2654,7 +2654,8 @@
 }
 
 binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
-        const std::string& instructionSet, const std::optional<std::string>& outputPath) {
+        const std::string& instructionSet, const std::optional<std::string>& outputPath,
+        int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
@@ -2664,8 +2665,8 @@
     const char* instruction_set = instructionSet.c_str();
     const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
 
-    bool res = delete_odex(apk_path, instruction_set, oat_dir);
-    return res ? ok() : error();
+    *_aidl_return = delete_odex(apk_path, instruction_set, oat_dir);
+    return *_aidl_return == -1 ? error() : ok();
 }
 
 // This kernel feature is experimental.
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..3127be6 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -122,7 +122,7 @@
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
     binder::Status mergeProfiles(int32_t uid, const std::string& packageName,
-            const std::string& profileName, bool* _aidl_return);
+            const std::string& profileName, int* _aidl_return);
     binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
             const std::string& profileName, const std::string& codePath, bool* _aidl_return);
     binder::Status copySystemProfile(const std::string& systemProfile,
@@ -147,7 +147,7 @@
     binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
-            const std::optional<std::string>& outputPath);
+            const std::optional<std::string>& outputPath, int64_t* _aidl_return);
     binder::Status installApkVerity(const std::string& filePath,
             android::base::unique_fd verityInput, int32_t contentSize);
     binder::Status assertFsverityRootHashMatches(const std::string& filePath,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 4ac70a4..816e508 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -71,7 +71,7 @@
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
-    boolean mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName);
+    int mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName);
     boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String  profileName,
             @utf8InCpp String codePath);
     boolean copySystemProfile(@utf8InCpp String systemProfile, int uid,
@@ -93,7 +93,7 @@
             @utf8InCpp String toBase);
     void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @utf8InCpp String outputPath);
-    void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+    long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @nullable @utf8InCpp String outputPath);
     void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput,
             int contentSize);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index cc0434d..15f0c5b 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -53,6 +53,7 @@
 #include "execv_helper.h"
 #include "globals.h"
 #include "installd_deps.h"
+#include "installd_constants.h"
 #include "otapreopt_utils.h"
 #include "run_dex2oat.h"
 #include "unique_file.h"
@@ -416,11 +417,12 @@
 
 static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0;
 static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_NOT_ENOUGH_DELTA = 2;
 static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 3;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 4;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 5;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS = 6;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_EMPTY_PROFILES = 7;
 
 class RunProfman : public ExecVHelper {
   public:
@@ -555,15 +557,7 @@
     std::vector<unique_fd> apk_fds_;
 };
 
-
-
-// Decides if profile guided compilation is needed or not based on existing profiles.
-// The location is the package name for primary apks or the dex path for secondary dex files.
-// Returns true if there is enough information in the current profiles that makes it
-// worth to recompile the given location.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-static bool analyze_profiles(uid_t uid, const std::string& package_name,
+static int analyze_profiles(uid_t uid, const std::string& package_name,
         const std::string& location, bool is_secondary_dex) {
     std::vector<unique_fd> profiles_fd;
     unique_fd reference_profile_fd;
@@ -572,7 +566,7 @@
     if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
         // Skip profile guided compilation because no profiles were found.
         // Or if the reference profile info couldn't be opened.
-        return false;
+        return PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES;
     }
 
     RunProfman profman_merge;
@@ -594,6 +588,7 @@
     /* parent */
     int return_code = wait_child(pid);
     bool need_to_compile = false;
+    bool empty_profiles = false;
     bool should_clear_current_profiles = false;
     bool should_clear_reference_profile = false;
     if (!WIFEXITED(return_code)) {
@@ -606,11 +601,17 @@
                 should_clear_current_profiles = true;
                 should_clear_reference_profile = false;
                 break;
-            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_NOT_ENOUGH_DELTA:
                 need_to_compile = false;
                 should_clear_current_profiles = false;
                 should_clear_reference_profile = false;
                 break;
+            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_EMPTY_PROFILES:
+                need_to_compile = false;
+                empty_profiles = true;
+                should_clear_current_profiles = false;
+                should_clear_reference_profile = false;
+                break;
             case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
                 LOG(WARNING) << "Bad profiles for location " << location;
                 need_to_compile = false;
@@ -653,16 +654,29 @@
     if (should_clear_reference_profile) {
         clear_reference_profile(package_name, location, is_secondary_dex);
     }
-    return need_to_compile;
+    int result = 0;
+    if (need_to_compile) {
+        result = PROFILES_ANALYSIS_OPTIMIZE;
+    } else if (empty_profiles) {
+        result = PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES;
+    } else {
+        result = PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
+    }
+    return result;
 }
 
 // Decides if profile guided compilation is needed or not based on existing profiles.
-// The analysis is done for the primary apks of the given package.
-// Returns true if there is enough information in the current profiles that makes it
-// worth to recompile the package.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-bool analyze_primary_profiles(uid_t uid, const std::string& package_name,
+// The analysis is done for a single profile name (which corresponds to a single code path).
+//
+// Returns PROFILES_ANALYSIS_OPTIMIZE if there is enough information in the current profiles
+// that makes it worth to recompile the package.
+// If the return value is PROFILES_ANALYSIS_OPTIMIZE all the current profiles would have been
+// merged into the reference profiles accessible with open_reference_profile().
+//
+// Return PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA if the package should not optimize.
+// As a special case returns PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES if all profiles are
+// empty.
+int analyze_primary_profiles(uid_t uid, const std::string& package_name,
         const std::string& profile_name) {
     return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false);
 }
@@ -1166,7 +1180,7 @@
                       int zip_fd,
                       const std::string& instruction_set,
                       const std::string& compiler_filter,
-                      bool profile_was_updated,
+                      int profile_analysis_result,
                       bool downgrade,
                       const char* class_loader_context,
                       const std::string& class_loader_context_fds) {
@@ -1182,7 +1196,8 @@
         std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd);
         std::string isa_arg = "--isa=" + instruction_set;
         std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter;
-        const char* assume_profile_changed = "--assume-profile-changed";
+        std::string profile_analysis_arg = "--profile-analysis-result="
+                + std::to_string(profile_analysis_result);
         const char* downgrade_flag = "--downgrade";
         std::string class_loader_context_arg = "--class-loader-context=";
         if (class_loader_context != nullptr) {
@@ -1204,9 +1219,8 @@
             AddArg(vdex_fd_arg);
         }
         AddArg(zip_fd_arg);
-        if (profile_was_updated) {
-            AddArg(assume_profile_changed);
-        }
+        AddArg(profile_analysis_arg);
+
         if (downgrade) {
             AddArg(downgrade_flag);
         }
@@ -1578,7 +1592,7 @@
         }
 
         // Analyze profiles.
-        bool profile_was_updated = analyze_profiles(uid, pkgname, dex_path,
+        int profile_analysis_result = analyze_profiles(uid, pkgname, dex_path,
                 /*is_secondary_dex*/true);
 
         // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
@@ -1589,7 +1603,8 @@
                                               oat_file_fd.get(),
                                               zip_fd.get(),
                                               instruction_set,
-                                              compiler_filter, profile_was_updated,
+                                              compiler_filter,
+                                              profile_analysis_result,
                                               downgrade,
                                               class_loader_context,
                                               join_fds(context_zip_fds));
@@ -2237,38 +2252,52 @@
     return success;
 }
 
-bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+int64_t delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
     // Delete the oat/odex file.
     char out_path[PKG_PATH_MAX];
     if (!create_oat_out_path(apk_path, instruction_set, oat_dir,
             /*is_secondary_dex*/false, out_path)) {
-        return false;
+        LOG(ERROR) << "Cannot create apk path for " << apk_path;
+        return -1;
     }
 
     // In case of a permission failure report the issue. Otherwise just print a warning.
-    auto unlink_and_check = [](const char* path) -> bool {
-        int result = unlink(path);
-        if (result != 0) {
-            if (errno == EACCES || errno == EPERM) {
-                PLOG(ERROR) << "Could not unlink " << path;
-                return false;
+    auto unlink_and_check = [](const char* path) -> int64_t {
+        struct stat file_stat;
+        if (stat(path, &file_stat) != 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "Could not stat " << path;
+                return -1;
             }
-            PLOG(WARNING) << "Could not unlink " << path;
+            return 0;
         }
-        return true;
+
+        if (unlink(path) != 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "Could not unlink " << path;
+                return -1;
+            }
+        }
+        return static_cast<int64_t>(file_stat.st_size);
     };
 
     // Delete the oat/odex file.
-    bool return_value_oat = unlink_and_check(out_path);
+    int64_t return_value_oat = unlink_and_check(out_path);
 
     // Derive and delete the app image.
-    bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
+    int64_t return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
 
     // Derive and delete the vdex file.
-    bool return_value_vdex = unlink_and_check(create_vdex_filename(out_path).c_str());
+    int64_t return_value_vdex = unlink_and_check(create_vdex_filename(out_path).c_str());
 
-    // Report success.
-    return return_value_oat && return_value_art && return_value_vdex;
+    // Report result
+    if (return_value_oat == -1
+            || return_value_art == -1
+            || return_value_vdex == -1) {
+        return -1;
+    }
+
+    return return_value_oat + return_value_art + return_value_vdex;
 }
 
 static bool is_absolute_path(const std::string& path) {
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index d35953c..5a637b1 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -54,15 +54,20 @@
 // Clear all current profiles identified by the given profile name (all users).
 bool clear_primary_current_profiles(const std::string& pkgname, const std::string& profile_name);
 
-// Decide if profile guided compilation is needed or not based on existing profiles.
+// Decides if profile guided compilation is needed or not based on existing profiles.
 // The analysis is done for a single profile name (which corresponds to a single code path).
-// Returns true if there is enough information in the current profiles that makes it
-// worth to recompile the package.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-bool analyze_primary_profiles(uid_t uid,
-                              const std::string& pkgname,
-                              const std::string& profile_name);
+//
+// Returns PROFILES_ANALYSIS_OPTIMIZE if there is enough information in the current profiles
+// that makes it worth to recompile the package.
+// If the return value is PROFILES_ANALYSIS_OPTIMIZE all the current profiles would have been
+// merged into the reference profiles accessible with open_reference_profile().
+//
+// Return PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA if the package should not optimize.
+// As a special case returns PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES if all profiles are
+// empty.
+int analyze_primary_profiles(uid_t uid,
+                             const std::string& pkgname,
+                             const std::string& profile_name);
 
 // Create a snapshot of the profile information for the given package profile.
 // If appId is -1, the method creates the profile snapshot for the boot image.
@@ -104,7 +109,8 @@
                          const std::string& code_path,
                          const std::optional<std::string>& dex_metadata);
 
-bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
+// Returns the total bytes that were freed, or -1 in case of errors.
+int64_t delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
 
 bool reconcile_secondary_dex_file(const std::string& dex_path,
         const std::string& pkgname, int uid, const std::vector<std::string>& isas,
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index b5ee481..00d8441 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -77,6 +77,12 @@
 constexpr int FLAG_STORAGE_DE = 1 << 0;
 constexpr int FLAG_STORAGE_CE = 1 << 1;
 
+// TODO: import them from dexoptanalyzer.h
+// NOTE: keep in sync with Installer.java
+constexpr int PROFILES_ANALYSIS_OPTIMIZE                     = 1;
+constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA    = 2;
+constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3;
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 }  // namespace installd
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index a27fd10..e847626 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -324,6 +324,12 @@
 
     AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"));
     AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"));
+
+    // Enable compiling dex files in isolation on low ram devices.
+    // It takes longer but reduces the memory footprint.
+    if (GetBoolProperty("ro.config.low_ram", false)) {
+      AddArg("--compile-individually");
+    }
 }
 
 void RunDex2Oat::Exec(int exit_code) {
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 216347e..7e7e513 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -38,6 +38,7 @@
 #include "binder_test_utils.h"
 #include "dexopt.h"
 #include "InstalldNativeService.h"
+#include "installd_constants.h"
 #include "globals.h"
 #include "tests/test_utils.h"
 #include "utils.h"
@@ -517,7 +518,8 @@
         // Check the access to the compiler output.
         //  - speed-profile artifacts are not world-wide readable.
         //  - files are owned by the system uid.
-        std::string odex = GetPrimaryDexArtifact(oat_dir, apk_path_, "odex");
+        std::string odex = GetPrimaryDexArtifact(oat_dir, apk_path_,
+                oat_dir == nullptr ? "dex" : "odex");
         std::string vdex = GetPrimaryDexArtifact(oat_dir, apk_path_, "vdex");
         std::string art = GetPrimaryDexArtifact(oat_dir, apk_path_, "art");
 
@@ -545,7 +547,7 @@
                 }
             }
             return android_data_dir + DALVIK_CACHE + '/' + kRuntimeIsa + "/" + path
-                    + "@classes.dex";
+                    + "@classes." + type;
         } else {
             std::string::size_type name_end = dex_path.rfind('.');
             std::string::size_type name_start = dex_path.rfind('/');
@@ -553,6 +555,53 @@
                     dex_path.substr(name_start + 1, name_end - name_start) + type;
         }
     }
+
+    int64_t GetSize(const std::string& path) {
+        struct stat file_stat;
+        if (stat(path.c_str(), &file_stat) == 0) {
+            return static_cast<int64_t>(file_stat.st_size);
+        }
+        PLOG(ERROR) << "Cannot stat path: " << path;
+        return -1;
+    }
+
+    void TestDeleteOdex(bool in_dalvik_cache) {
+        const char* oat_dir = in_dalvik_cache ? nullptr : app_oat_dir_.c_str();
+        CompilePrimaryDexOk(
+                "speed-profile",
+                DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC
+                        | DEXOPT_GENERATE_APP_IMAGE,
+                oat_dir,
+                kTestAppGid,
+                DEX2OAT_FROM_SCRATCH,
+                /*binder_result=*/nullptr,
+                empty_dm_file_.c_str());
+
+
+        int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
+                in_dalvik_cache ? "dex" : "odex"));
+        int64_t vdex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_, "vdex"));
+        int64_t art_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_, "art"));
+
+        LOG(ERROR) << "test odex " << odex_size;
+        LOG(ERROR) << "test vdex_size " << vdex_size;
+        LOG(ERROR) << "test art_size " << art_size;
+        int64_t expected_bytes_freed = odex_size + vdex_size + art_size;
+
+        int64_t bytes_freed;
+        binder::Status result = service_->deleteOdex(
+            apk_path_,
+            kRuntimeIsa,
+            in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()),
+            &bytes_freed);
+        ASSERT_TRUE(result.isOk()) << result.toString8().c_str();
+
+        ASSERT_GE(odex_size, 0);
+        ASSERT_GE(vdex_size, 0);
+        ASSERT_GE(art_size, 0);
+
+        ASSERT_EQ(expected_bytes_freed, bytes_freed);
+    }
 };
 
 
@@ -701,6 +750,16 @@
                         empty_dm_file_.c_str());
 }
 
+TEST_F(DexoptTest, DeleteDexoptArtifactsData) {
+    LOG(INFO) << "DeleteDexoptArtifactsData";
+    TestDeleteOdex(/*in_dalvik_cache=*/ false);
+}
+
+TEST_F(DexoptTest, DeleteDexoptArtifactsDalvikCache) {
+    LOG(INFO) << "DeleteDexoptArtifactsDalvikCache";
+    TestDeleteOdex(/*in_dalvik_cache=*/ true);
+}
+
 TEST_F(DexoptTest, ResolveStartupConstStrings) {
     LOG(INFO) << "DexoptDex2oatResolveStartupStrings";
     const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings";
@@ -951,14 +1010,14 @@
 
     void mergePackageProfiles(const std::string& package_name,
                               const std::string& code_path,
-                              bool expected_result) {
-        bool result;
+                              int expected_result) {
+        int result;
         ASSERT_BINDER_SUCCESS(service_->mergeProfiles(
                 kTestAppUid, package_name, code_path, &result));
         ASSERT_EQ(expected_result, result);
 
-        if (!expected_result) {
-            // Do not check the files if we expect to fail.
+        // There's nothing to check if the files are empty.
+        if (result == PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) {
             return;
         }
 
@@ -1077,7 +1136,7 @@
     LOG(INFO) << "ProfileMergeOk";
 
     SetupProfiles(/*setup_ref*/ true);
-    mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true);
+    mergePackageProfiles(package_name_, "primary.prof", PROFILES_ANALYSIS_OPTIMIZE);
 }
 
 // The reference profile is created on the fly. We need to be able to
@@ -1086,14 +1145,15 @@
     LOG(INFO) << "ProfileMergeOkNoReference";
 
     SetupProfiles(/*setup_ref*/ false);
-    mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true);
+    mergePackageProfiles(package_name_, "primary.prof", PROFILES_ANALYSIS_OPTIMIZE);
 }
 
 TEST_F(ProfileTest, ProfileMergeFailWrongPackage) {
     LOG(INFO) << "ProfileMergeFailWrongPackage";
 
     SetupProfiles(/*setup_ref*/ true);
-    mergePackageProfiles("not.there", "primary.prof", /*expected_result*/ false);
+    mergePackageProfiles("not.there", "primary.prof",
+            PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES);
 }
 
 TEST_F(ProfileTest, ProfileDirOk) {
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index bc99f4d..a5f98c2 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -142,12 +142,10 @@
         }
     }
 
-    PipeRelay relay(out, err, interfaceName, instanceName);
-
-    if (relay.initCheck() != OK) {
-        std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
-        err << msg << std::endl;
-        LOG(ERROR) << msg;
+    auto relay = PipeRelay::create(out, err, interfaceName + "/" + instanceName);
+    if (!relay.ok()) {
+        err << "Unable to create PipeRelay: " << relay.error() << std::endl;
+        LOG(ERROR) << "Unable to create PipeRelay: " << relay.error();
         return IO_ERROR;
     }
 
@@ -155,7 +153,7 @@
         native_handle_create(1 /* numFds */, 0 /* numInts */),
         native_handle_delete);
 
-    fdHandle->data[0] = relay.fd();
+    fdHandle->data[0] = relay.value()->fd().get();
 
     hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options));
 
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 4e97636..0c3fb96 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -16,143 +16,93 @@
 
 #include "PipeRelay.h"
 
-#include <sys/select.h>
-#include <sys/time.h>
+#include <sys/poll.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <atomic>
+#include <chrono>
+#include <optional>
 
-#include <utils/Thread.h>
+#include <android-base/unique_fd.h>
+
+using android::base::borrowed_fd;
+using android::base::Result;
+using android::base::unique_fd;
+using std::chrono_literals::operator""ms;
 
 namespace android {
 namespace lshal {
-
-static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
-
-static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
-    auto dot = interfaceName.rfind(".");
-    if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
-    return "RelayThread_" + interfaceName + "_" + instanceName;
+Result<std::unique_ptr<PipeRelay>> PipeRelay::create(std::ostream& os,
+                                                     const NullableOStream<std::ostream>& err,
+                                                     const std::string& fqName) {
+    auto pipeRelay = std::unique_ptr<PipeRelay>(new PipeRelay());
+    unique_fd rfd;
+    if (!android::base::Pipe(&rfd, &pipeRelay->mWrite)) {
+        return android::base::ErrnoError() << "pipe()";
+    }
+    // Workaround for b/111997867: need a separate FD trigger because rfd can't receive POLLHUP
+    // when the write end is closed after the write end was sent through hwbinder.
+    unique_fd rfdTrigger;
+    if (!android::base::Pipe(&rfdTrigger, &pipeRelay->mWriteTrigger)) {
+        return android::base::ErrnoError() << "pipe() for trigger";
+    }
+    pipeRelay->mThread =
+            std::make_unique<std::thread>(&PipeRelay::thread, std::move(rfd), std::move(rfdTrigger),
+                                          &os, &err, fqName);
+    return pipeRelay;
 }
 
-struct PipeRelay::RelayThread : public Thread {
-    explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
-                         const std::string &fqName);
+void PipeRelay::thread(unique_fd rfd, unique_fd rfdTrigger, std::ostream* out,
+                       const NullableOStream<std::ostream>* err, std::string fqName) {
+    while (true) {
+        pollfd pfd[2];
+        pfd[0] = {.fd = rfd.get(), .events = POLLIN};
+        pfd[1] = {.fd = rfdTrigger.get(), .events = 0};
 
-    bool threadLoop() override;
-    void setFinished();
-
-private:
-    int mFd;
-    std::ostream &mOutStream;
-    NullableOStream<std::ostream> mErrStream;
-
-    // If we were to use requestExit() and exitPending() instead, threadLoop()
-    // may not run at all by the time ~PipeRelay is called (i.e. debug() has
-    // returned from HAL). By using our own flag, we ensure that select() and
-    // read() are executed until data are drained.
-    std::atomic_bool mFinished;
-
-    std::string mFqName;
-
-    DISALLOW_COPY_AND_ASSIGN(RelayThread);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
-                                    const NullableOStream<std::ostream> &err,
-                                    const std::string &fqName)
-      : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
-
-bool PipeRelay::RelayThread::threadLoop() {
-    char buffer[1024];
-
-    fd_set set;
-    FD_ZERO(&set);
-    FD_SET(mFd, &set);
-
-    struct timeval timeout = READ_TIMEOUT;
-
-    int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
-    if (res < 0) {
-        mErrStream << "debug " << mFqName << ": select() failed";
-        return false;
-    }
-
-    if (res == 0 || !FD_ISSET(mFd, &set)) {
-        if (mFinished) {
-            mErrStream << "debug " << mFqName
-                       << ": timeout reading from pipe, output may be truncated.";
-            return false;
+        int pollRes = poll(pfd, arraysize(pfd), -1 /* infinite timeout */);
+        if (pollRes < 0) {
+            int savedErrno = errno;
+            (*err) << "debug " << fqName << ": poll() failed: " << strerror(savedErrno)
+                   << std::endl;
+            break;
         }
-        // timeout, but debug() has not returned, so wait for HAL to finish.
-        return true;
-    }
 
-    // FD_ISSET(mFd, &set) == true. Data available, start reading
-    ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
-
-    if (n < 0) {
-        mErrStream << "debug " << mFqName << ": read() failed";
-    }
-
-    if (n <= 0) {
-        return false;
-    }
-
-    mOutStream.write(buffer, n);
-
-    return true;
-}
-
-void PipeRelay::RelayThread::setFinished() {
-    mFinished = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
-                     const std::string &interfaceName, const std::string &instanceName)
-      : mInitCheck(NO_INIT) {
-    int res = pipe(mFds);
-
-    if (res < 0) {
-        mInitCheck = -errno;
-        return;
-    }
-
-    mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
-    mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
-}
-
-void PipeRelay::CloseFd(int *fd) {
-    if (*fd >= 0) {
-        close(*fd);
-        *fd = -1;
+        if (pfd[0].revents & POLLIN) {
+            char buffer[1024];
+            ssize_t n = TEMP_FAILURE_RETRY(read(rfd.get(), buffer, sizeof(buffer)));
+            if (n < 0) {
+                int savedErrno = errno;
+                (*err) << "debug " << fqName << ": read() failed: " << strerror(savedErrno)
+                       << std::endl;
+                break;
+            }
+            if (n == 0) {
+                (*err) << "Warning: debug " << fqName << ": poll() indicates POLLIN but no data"
+                       << std::endl;
+                continue;
+            }
+            out->write(buffer, n);
+        }
+        if (pfd[0].revents & POLLHUP) {
+            break;
+        }
+        if (pfd[1].revents & POLLHUP) {
+            // ~PipeRelay is called on the main thread. |mWrite| has been flushed and closed.
+            // Ensure that our read end of the pipe doesn't have pending data, then exit.
+            if ((pfd[0].revents & POLLIN) == 0) {
+                break;
+            }
+        }
     }
 }
 
 PipeRelay::~PipeRelay() {
-    CloseFd(&mFds[1]);
-
-    if (mThread != nullptr) {
-        mThread->setFinished();
+    mWrite.reset();
+    mWriteTrigger.reset();
+    if (mThread != nullptr && mThread->joinable()) {
         mThread->join();
-        mThread.clear();
     }
-
-    CloseFd(&mFds[0]);
 }
 
-status_t PipeRelay::initCheck() const {
-    return mInitCheck;
-}
-
-int PipeRelay::fd() const {
-    return mFds[1];
-}
-
-}  // namespace lshal
-}  // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index bd994b4..45ba982 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -16,42 +16,43 @@
 
 #pragma once
 
+#include <thread>
+
 #include <android-base/macros.h>
-#include <ostream>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
+#include <ostream>
 
 #include "NullableOStream.h"
 
 namespace android {
 namespace lshal {
 
-/* Creates an AF_UNIX socketpair and spawns a thread that relays any data
+/**
+ * Creates a pipe and spawns a thread that relays any data
  * written to the "write"-end of the pair to the specified output stream "os".
  */
 struct PipeRelay {
-    explicit PipeRelay(std::ostream& os,
-                       const NullableOStream<std::ostream>& err,
-                       const std::string& interfaceName,
-                       const std::string& instanceName);
+    static android::base::Result<std::unique_ptr<PipeRelay>> create(
+            std::ostream& os, const NullableOStream<std::ostream>& err, const std::string& fqName);
     ~PipeRelay();
 
-    status_t initCheck() const;
-
     // Returns the file descriptor corresponding to the "write"-end of the
     // connection.
-    int fd() const;
+    android::base::borrowed_fd fd() const { return mWrite; }
 
 private:
-    struct RelayThread;
-
-    status_t mInitCheck;
-    int mFds[2];
-    sp<RelayThread> mThread;
-
-    static void CloseFd(int *fd);
-
+    PipeRelay() = default;
     DISALLOW_COPY_AND_ASSIGN(PipeRelay);
+    static void thread(android::base::unique_fd rfd, android::base::unique_fd rfdTrigger,
+                       std::ostream* out, const NullableOStream<std::ostream>* err,
+                       std::string fqName);
+
+    android::base::unique_fd mWrite;
+    android::base::unique_fd mWriteTrigger;
+    std::unique_ptr<std::thread> mThread;
 };
 
 }  // namespace lshal
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index b9c8d20..da570f3 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -146,6 +146,15 @@
         darwin: {
             enabled: false,
         },
+        host: {
+            static_libs: [
+                "libbase",
+            ],
+            srcs: [
+                "ServiceManagerHost.cpp",
+                "UtilsHost.cpp",
+            ],
+        },
     },
 
     aidl: {
@@ -257,6 +266,32 @@
     },
 }
 
+// TODO(b/184872979): remove once the Rust API is created.
+cc_library {
+    name: "libbinder_rpc_unstable",
+    srcs: ["libbinder_rpc_unstable.cpp"],
+    defaults: ["libbinder_ndk_host_user"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libbinder_ndk",
+        "libutils",
+    ],
+
+    // enumerate stable entry points, for apex use
+    stubs: {
+        symbol_file: "libbinder_rpc_unstable.map.txt",
+    },
+
+    // This library is intentionally limited to these targets, and it will be removed later.
+    // Do not expand the visibility.
+    visibility: [
+        "//packages/modules/Virtualization/authfs:__subpackages__",
+        "//packages/modules/Virtualization/compos",
+        "//packages/modules/Virtualization/microdroid",
+    ],
+}
+
 // libbinder historically contained additional interfaces that provided specific
 // functionality in the platform but have nothing to do with binder itself. These
 // are moved out of libbinder in order to avoid the overhead of their vtables.
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d811b17..628381c 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -179,6 +179,17 @@
     return transact(SET_RPC_CLIENT_TRANSACTION, data, &reply);
 }
 
+void IBinder::withLock(const std::function<void()>& doWithLock) {
+    BBinder* local = localBinder();
+    if (local) {
+        local->withLock(doWithLock);
+        return;
+    }
+    BpBinder* proxy = this->remoteBinder();
+    LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote");
+    proxy->withLock(doWithLock);
+}
+
 // ---------------------------------------------------------------------------
 
 class BBinder::RpcServerLink : public IBinder::DeathRecipient {
@@ -311,15 +322,13 @@
     return NO_ERROR;
 }
 
-void BBinder::attachObject(
-    const void* objectID, void* object, void* cleanupCookie,
-    object_cleanup_func func)
-{
+void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
+                            object_cleanup_func func) {
     Extras* e = getOrCreateExtras();
-    if (!e) return; // out of memory
+    LOG_ALWAYS_FATAL_IF(!e, "no memory");
 
     AutoMutex _l(e->mLock);
-    e->mObjects.attach(objectID, object, cleanupCookie, func);
+    return e->mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
 void* BBinder::findObject(const void* objectID) const
@@ -331,13 +340,20 @@
     return e->mObjects.find(objectID);
 }
 
-void BBinder::detachObject(const void* objectID)
-{
+void* BBinder::detachObject(const void* objectID) {
     Extras* e = mExtras.load(std::memory_order_acquire);
-    if (!e) return;
+    if (!e) return nullptr;
 
     AutoMutex _l(e->mLock);
-    e->mObjects.detach(objectID);
+    return e->mObjects.detach(objectID);
+}
+
+void BBinder::withLock(const std::function<void()>& doWithLock) {
+    Extras* e = getOrCreateExtras();
+    LOG_ALWAYS_FATAL_IF(!e, "no memory");
+
+    AutoMutex _l(e->mLock);
+    doWithLock();
 }
 
 BBinder* BBinder::localBinder()
@@ -354,9 +370,9 @@
 
 void BBinder::setRequestingSid(bool requestingSid)
 {
-    ALOGW_IF(mParceled,
-             "setRequestingSid() should not be called after a binder object "
-             "is parceled/sent to another process");
+    LOG_ALWAYS_FATAL_IF(mParceled,
+                        "setRequestingSid() should not be called after a binder object "
+                        "is parceled/sent to another process");
 
     Extras* e = mExtras.load(std::memory_order_acquire);
 
@@ -380,9 +396,9 @@
 }
 
 void BBinder::setMinSchedulerPolicy(int policy, int priority) {
-    ALOGW_IF(mParceled,
-             "setMinSchedulerPolicy() should not be called after a binder object "
-             "is parceled/sent to another process");
+    LOG_ALWAYS_FATAL_IF(mParceled,
+                        "setMinSchedulerPolicy() should not be called after a binder object "
+                        "is parceled/sent to another process");
 
     switch (policy) {
     case SCHED_NORMAL:
@@ -431,9 +447,9 @@
 }
 
 void BBinder::setInheritRt(bool inheritRt) {
-    ALOGW_IF(mParceled,
-             "setInheritRt() should not be called after a binder object "
-             "is parceled/sent to another process");
+    LOG_ALWAYS_FATAL_IF(mParceled,
+                        "setInheritRt() should not be called after a binder object "
+                        "is parceled/sent to another process");
 
     Extras* e = mExtras.load(std::memory_order_acquire);
 
@@ -454,9 +470,9 @@
 }
 
 void BBinder::setExtension(const sp<IBinder>& extension) {
-    ALOGW_IF(mParceled,
-             "setExtension() should not be called after a binder object "
-             "is parceled/sent to another process");
+    LOG_ALWAYS_FATAL_IF(mParceled,
+                        "setExtension() should not be called after a binder object "
+                        "is parceled/sent to another process");
 
     Extras* e = getOrCreateExtras();
     e->mExtension = extension;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 5e44a0f..3099296 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -61,22 +61,22 @@
     kill();
 }
 
-void BpBinder::ObjectManager::attach(
-    const void* objectID, void* object, void* cleanupCookie,
-    IBinder::object_cleanup_func func)
-{
+void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie,
+                                      IBinder::object_cleanup_func func) {
     entry_t e;
     e.object = object;
     e.cleanupCookie = cleanupCookie;
     e.func = func;
 
-    if (mObjects.indexOfKey(objectID) >= 0) {
-        ALOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use",
-                objectID, this,  object);
-        return;
+    if (ssize_t idx = mObjects.indexOfKey(objectID); idx >= 0) {
+        ALOGI("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object "
+              "ID already in use",
+              objectID, this, object);
+        return mObjects[idx].object;
     }
 
     mObjects.add(objectID, e);
+    return nullptr;
 }
 
 void* BpBinder::ObjectManager::find(const void* objectID) const
@@ -86,9 +86,12 @@
     return mObjects.valueAt(i).object;
 }
 
-void BpBinder::ObjectManager::detach(const void* objectID)
-{
-    mObjects.removeItem(objectID);
+void* BpBinder::ObjectManager::detach(const void* objectID) {
+    ssize_t idx = mObjects.indexOfKey(objectID);
+    if (idx < 0) return nullptr;
+    void* value = mObjects[idx].object;
+    mObjects.removeItemsAt(idx, 1);
+    return value;
 }
 
 void BpBinder::ObjectManager::kill()
@@ -406,14 +409,11 @@
     recipient->binderDied(wp<BpBinder>::fromExisting(this));
 }
 
-
-void BpBinder::attachObject(
-    const void* objectID, void* object, void* cleanupCookie,
-    object_cleanup_func func)
-{
+void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
+                             object_cleanup_func func) {
     AutoMutex _l(mLock);
     ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
-    mObjects.attach(objectID, object, cleanupCookie, func);
+    return mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
 void* BpBinder::findObject(const void* objectID) const
@@ -422,10 +422,14 @@
     return mObjects.find(objectID);
 }
 
-void BpBinder::detachObject(const void* objectID)
-{
+void* BpBinder::detachObject(const void* objectID) {
     AutoMutex _l(mLock);
-    mObjects.detach(objectID);
+    return mObjects.detach(objectID);
+}
+
+void BpBinder::withLock(const std::function<void()>& doWithLock) {
+    AutoMutex _l(mLock);
+    doWithLock();
 }
 
 BpBinder* BpBinder::remoteBinder()
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index d421060..2175fd4 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -35,6 +35,8 @@
 
 #ifdef __ANDROID__
 #include <cutils/properties.h>
+#else
+#include "ServiceManagerHost.h"
 #endif
 
 #include "Static.h"
@@ -84,8 +86,19 @@
     IBinder* onAsBinder() override {
         return IInterface::asBinder(mTheRealServiceManager).get();
     }
-private:
+
+protected:
     sp<AidlServiceManager> mTheRealServiceManager;
+
+    // Directly get the service in a way that, for lazy services, requests the service to be started
+    // if it is not currently started. This way, calls directly to ServiceManagerShim::getService
+    // will still have the 5s delay that is expected by a large amount of Android code.
+    //
+    // When implementing ServiceManagerShim, use realGetService instead of
+    // mTheRealServiceManager->getService so that it can be overridden in ServiceManagerHostShim.
+    virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
+        return mTheRealServiceManager->getService(name, _aidl_return);
+    }
 };
 
 [[clang::no_destroy]] static std::once_flag gSmOnce;
@@ -319,7 +332,7 @@
     const std::string name = String8(name16).c_str();
 
     sp<IBinder> out;
-    if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+    if (Status status = realGetService(name, &out); !status.isOk()) {
         ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
               status.toString8().c_str());
         return nullptr;
@@ -363,7 +376,7 @@
         // - init gets death signal, but doesn't know it needs to restart
         //   the service
         // - we need to request service again to get it to start
-        if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+        if (Status status = realGetService(name, &out); !status.isOk()) {
             ALOGW("Failed to getService in waitForService on later try for %s: %s", name.c_str(),
                   status.toString8().c_str());
             return nullptr;
@@ -412,4 +425,38 @@
     return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
 }
 
+#ifndef __ANDROID__
+// ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API.
+// The internal implementation of the AIDL interface android::os::IServiceManager calls into
+// on-device service manager.
+class ServiceManagerHostShim : public ServiceManagerShim {
+public:
+    using ServiceManagerShim::ServiceManagerShim;
+    // ServiceManagerShim::getService is based on checkService, so no need to override it.
+    sp<IBinder> checkService(const String16& name) const override {
+        return getDeviceService({String8(name).c_str()});
+    }
+
+protected:
+    // Override realGetService for ServiceManagerShim::waitForService.
+    Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
+        *_aidl_return = getDeviceService({"-g", name});
+        return Status::ok();
+    }
+};
+sp<IServiceManager> createRpcDelegateServiceManager() {
+    auto binder = getDeviceService({"manager"});
+    if (binder == nullptr) {
+        ALOGE("getDeviceService(\"manager\") returns null");
+        return nullptr;
+    }
+    auto interface = AidlServiceManager::asInterface(binder);
+    if (interface == nullptr) {
+        ALOGE("getDeviceService(\"manager\") returns non service manager");
+        return nullptr;
+    }
+    return sp<ServiceManagerHostShim>::make(interface);
+}
+#endif
+
 } // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6165911..f66993f 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -40,9 +40,9 @@
 
     void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    bool tryUnregister();
+    bool tryUnregisterLocked();
 
-    void reRegister();
+    void reRegisterLocked();
 
 protected:
     Status onClients(const sp<IBinder>& service, bool clients) override;
@@ -59,6 +59,9 @@
         bool registered = true;
     };
 
+    bool registerServiceLocked(const sp<IBinder>& service, const std::string& name,
+                               bool allowIsolated, int dumpFlags);
+
     /**
      * Looks up a service guaranteed to be registered (service from onClients).
      */
@@ -68,7 +71,7 @@
      * Unregisters all services that we can. If we can't unregister all, re-register other
      * services.
      */
-    void tryShutdown();
+    void tryShutdownLocked();
 
     /**
      * Try to shutdown the process, unless:
@@ -76,7 +79,10 @@
      * - The active services count callback returns 'true', or
      * - Some services have clients.
      */
-    void maybeTryShutdown();
+    void maybeTryShutdownLocked();
+
+    // for below
+    std::mutex mMutex;
 
     // count of services with clients
     size_t mNumConnectedServices;
@@ -117,6 +123,13 @@
 
 bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
                                             bool allowIsolated, int dumpFlags) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return registerServiceLocked(service, name, allowIsolated, dumpFlags);
+}
+
+bool ClientCounterCallbackImpl::registerServiceLocked(const sp<IBinder>& service,
+                                                      const std::string& name, bool allowIsolated,
+                                                      int dumpFlags) {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     bool reRegister = mRegisteredServices.count(name) > 0;
@@ -164,14 +177,15 @@
 }
 
 void ClientCounterCallbackImpl::forcePersist(bool persist) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mForcePersist = persist;
     if (!mForcePersist) {
         // Attempt a shutdown in case the number of clients hit 0 while the flag was on
-        maybeTryShutdown();
+        maybeTryShutdownLocked();
     }
 }
 
-bool ClientCounterCallbackImpl::tryUnregister() {
+bool ClientCounterCallbackImpl::tryUnregisterLocked() {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     for (auto& [name, entry] : mRegisteredServices) {
@@ -187,15 +201,14 @@
     return true;
 }
 
-void ClientCounterCallbackImpl::reRegister() {
+void ClientCounterCallbackImpl::reRegisterLocked() {
     for (auto& [name, entry] : mRegisteredServices) {
         // re-register entry if not already registered
         if (entry.registered) {
             continue;
         }
 
-        if (!registerService(entry.service, name, entry.allowIsolated,
-                             entry.dumpFlags)) {
+        if (!registerServiceLocked(entry.service, name, entry.allowIsolated, entry.dumpFlags)) {
             // Must restart. Otherwise, clients will never be able to get a hold of this service.
             LOG_ALWAYS_FATAL("Bad state: could not re-register services");
         }
@@ -204,7 +217,7 @@
     }
 }
 
-void ClientCounterCallbackImpl::maybeTryShutdown() {
+void ClientCounterCallbackImpl::maybeTryShutdownLocked() {
     if (mForcePersist) {
         ALOGI("Shutdown prevented by forcePersist override flag.");
         return;
@@ -223,15 +236,12 @@
     // client count change event, try to shutdown the process if its services
     // have no clients.
     if (!handledInCallback && mNumConnectedServices == 0) {
-        tryShutdown();
+        tryShutdownLocked();
     }
 }
 
-/**
- * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
- * invocations could occur on different threads however.
- */
 Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
+    std::lock_guard<std::mutex> lock(mMutex);
     auto & [name, registered] = *assertRegisteredService(service);
     if (registered.clients == clients) {
         LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -252,23 +262,24 @@
     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
           mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
 
-    maybeTryShutdown();
+    maybeTryShutdownLocked();
     return Status::ok();
 }
 
- void ClientCounterCallbackImpl::tryShutdown() {
-     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+void ClientCounterCallbackImpl::tryShutdownLocked() {
+    ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
-    if (tryUnregister()) {
-         ALOGI("Unregistered all clients and exiting");
-         exit(EXIT_SUCCESS);
-     }
+    if (tryUnregisterLocked()) {
+        ALOGI("Unregistered all clients and exiting");
+        exit(EXIT_SUCCESS);
+    }
 
-    reRegister();
+    reRegisterLocked();
 }
 
 void ClientCounterCallbackImpl::setActiveServicesCallback(const std::function<bool(bool)>&
                                                           activeServicesCallback) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mActiveServicesCallback = activeServicesCallback;
 }
 
@@ -291,11 +302,15 @@
 }
 
 bool ClientCounterCallback::tryUnregister() {
-    return mImpl->tryUnregister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    return mImpl->tryUnregisterLocked();
 }
 
 void ClientCounterCallback::reRegister() {
-    mImpl->reRegister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    mImpl->reRegisterLocked();
 }
 
 }  // namespace internal
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index a8f3fa8..2a87ae4 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -278,7 +278,7 @@
                 return;
             }
 
-            RpcAddress sessionId = RpcAddress::zero();
+            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 4f55eef..ee5e8bb 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -198,6 +198,8 @@
     uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
     uint8_t* end = buffer + size;
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     status_t status;
     while ((status = triggerablePollRead(fd)) == OK) {
         ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index fd2eff6..b5eaaa3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -26,12 +26,28 @@
 #include "Debug.h"
 #include "RpcWireFormat.h"
 
+#include <random>
+
 #include <inttypes.h>
 
 namespace android {
 
 using base::ScopeGuard;
 
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake() {
+    static std::random_device r;
+    static std::mutex m;
+
+    unsigned num;
+    {
+        std::lock_guard<std::mutex> lock(m);
+        num = r();
+    }
+    if (num % 10 == 0) usleep(num % 1000);
+}
+#endif
+
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
@@ -260,6 +276,8 @@
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, connection->fd.get(),
                    hexString(data, size).c_str());
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot send %s at size %zu (too big)", what, size);
         (void)session->shutdownAndWait(false);
@@ -483,7 +501,7 @@
     delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
     (void)dataSize;
     LOG_ALWAYS_FATAL_IF(objects != nullptr);
-    LOG_ALWAYS_FATAL_IF(objectsCount, 0);
+    LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount);
 }
 
 status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 529dee5..8201eba 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -42,6 +42,15 @@
 #define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
 #endif
 
+#define RPC_FLAKE_PRONE false
+
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake();
+#define MAYBE_WAIT_IN_FLAKE_MODE rpcMaybeWaitToFlake()
+#else
+#define MAYBE_WAIT_IN_FLAKE_MODE do {} while (false)
+#endif
+
 /**
  * Abstracts away management of ref counts and the wire format from
  * RpcSession
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
new file mode 100644
index 0000000..07f5778
--- /dev/null
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "ServiceManagerHost.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
+
+#include "UtilsHost.h"
+
+namespace android {
+
+namespace {
+
+const void* kDeviceServiceExtraId = "DeviceServiceExtra";
+
+// Parse stdout of program execution to string. If any error, return 0.
+unsigned int parsePortNumber(const std::string& out, const std::string& what) {
+    auto trimmed = android::base::Trim(out);
+    unsigned int port = 0;
+    if (!android::base::ParseUint(trimmed, &port)) {
+        int savedErrno = errno;
+        ALOGE("%s is not a valid %s: %s", trimmed.c_str(), what.c_str(), strerror(savedErrno));
+        return 0;
+    }
+    if (port == 0) {
+        ALOGE("0 is not a valid %s", what.c_str());
+        return 0; // explicitly
+    }
+    return port;
+}
+
+// RAII object for adb forwarding
+class AdbForwarder {
+public:
+    AdbForwarder() = default;
+    static std::optional<AdbForwarder> forward(unsigned int devicePort);
+    AdbForwarder(AdbForwarder&& other) noexcept { (*this) = std::move(other); }
+    AdbForwarder& operator=(AdbForwarder&&) noexcept;
+    ~AdbForwarder();
+    [[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(AdbForwarder);
+    explicit AdbForwarder(unsigned int port) : mPort(port) {}
+    std::optional<unsigned int> mPort;
+};
+std::optional<AdbForwarder> AdbForwarder::forward(unsigned int devicePort) {
+    auto result =
+            execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr);
+    if (!result.ok()) {
+        ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort,
+              result.error().message().c_str());
+        return std::nullopt;
+    }
+    // Must end with exit code 0 (`has_value() && value() == 0`)
+    if (result->exitCode.value_or(1) != 0) {
+        ALOGE("Unable to run `adb forward tcp:0 tcp:%d`, command exits with %s", devicePort,
+              result->toString().c_str());
+        return std::nullopt;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("`adb forward tcp:0 tcp:%d` writes to stderr: %s", devicePort,
+                 result->stderr.c_str());
+    }
+
+    unsigned int hostPort = parsePortNumber(result->stdout, "host port");
+    if (hostPort == 0) return std::nullopt;
+
+    return AdbForwarder(hostPort);
+}
+
+AdbForwarder& AdbForwarder::operator=(AdbForwarder&& other) noexcept {
+    std::swap(mPort, other.mPort);
+    return *this;
+}
+
+AdbForwarder::~AdbForwarder() {
+    if (!mPort.has_value()) return;
+
+    auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr);
+    if (!result.ok()) {
+        ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort,
+              result.error().message().c_str());
+        return;
+    }
+    // Must end with exit code 0 (`has_value() && value() == 0`)
+    if (result->exitCode.value_or(1) != 0) {
+        ALOGE("Unable to run `adb forward --remove tcp:%d`, command exits with %s", *mPort,
+              result->toString().c_str());
+        return;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("`adb forward --remove tcp:%d` writes to stderr: %s", *mPort,
+                 result->stderr.c_str());
+    }
+
+    LOG_HOST("Successfully run `adb forward --remove tcp:%d`", *mPort);
+}
+
+void cleanupCommandResult(const void* id, void* obj, void* /* cookie */) {
+    LOG_ALWAYS_FATAL_IF(id != kDeviceServiceExtraId,
+                        "cleanupCommandResult invoked with mismatched ID %p, "
+                        "expected %p",
+                        id, kDeviceServiceExtraId);
+    auto ptr = static_cast<CommandResult*>(obj);
+    delete ptr;
+}
+
+} // namespace
+
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) {
+    std::vector<std::string> prefix{"adb", "shell", "servicedispatcher"};
+    serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end());
+
+    auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine);
+    if (!result.ok()) {
+        ALOGE("%s", result.error().message().c_str());
+        return nullptr;
+    }
+
+    // `servicedispatcher` process must be alive to keep the port open.
+    if (result->exitCode.has_value()) {
+        ALOGE("Command exits with: %s", result->toString().c_str());
+        return nullptr;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("servicedispatcher writes to stderr: %s", result->stderr.c_str());
+    }
+
+    if (!result->stdoutEndsWithNewLine()) {
+        ALOGE("Unexpected command result: %s", result->toString().c_str());
+        return nullptr;
+    }
+
+    unsigned int devicePort = parsePortNumber(result->stdout, "device port");
+    if (devicePort == 0) return nullptr;
+
+    auto forwardResult = AdbForwarder::forward(devicePort);
+    if (!forwardResult.has_value()) {
+        return nullptr;
+    }
+    LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
+
+    auto rpcSession = RpcSession::make();
+    if (!rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort())) {
+        ALOGE("Unable to set up inet client on host port %u", *forwardResult->hostPort());
+        return nullptr;
+    }
+    auto binder = rpcSession->getRootObject();
+    if (binder == nullptr) {
+        ALOGE("RpcSession::getRootObject returns nullptr");
+        return nullptr;
+    }
+    binder->attachObject(kDeviceServiceExtraId,
+                         static_cast<void*>(new CommandResult(std::move(*result))), nullptr,
+                         &cleanupCommandResult);
+    return binder;
+}
+
+} // namespace android
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
new file mode 100644
index 0000000..e59724c
--- /dev/null
+++ b/libs/binder/ServiceManagerHost.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android/os/IServiceManager.h>
+
+namespace android {
+
+// Get a service on device by running servicedispatcher with the given args, e.g.
+//     getDeviceService({"foo"});
+// Return nullptr on any error.
+// When the returned binder object is destroyed, remove adb forwarding and kills
+// the long-running servicedispatcher process.
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs);
+
+} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index b58d919..748fa72 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,7 +19,7 @@
       "name": "binderTextOutputTest"
     },
     {
-      "name": "binderParcelTest"
+      "name": "binderUnitTest"
     },
     {
       "name": "binderLibTest"
@@ -31,6 +31,9 @@
       "name": "binderStabilityTest"
     },
     {
+      "name": "binderUtilsHostTest"
+    },
+    {
       "name": "libbinder_ndk_unit_test"
     },
     {
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
new file mode 100644
index 0000000..e524dab
--- /dev/null
+++ b/libs/binder/UtilsHost.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 "UtilsHost.h"
+
+#include <poll.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <sstream>
+
+#include <log/log.h>
+
+namespace android {
+
+CommandResult::~CommandResult() {
+    if (!pid.has_value()) return;
+    if (*pid == 0) {
+        ALOGW("%s: PID is unexpectedly 0, won't kill it", __PRETTY_FUNCTION__);
+        return;
+    }
+
+    ALOGE_IF(kill(*pid, SIGKILL) != 0, "kill(%d): %s", *pid, strerror(errno));
+
+    while (pid.has_value()) {
+        int status;
+        LOG_HOST("%s: Waiting for PID %d to exit.", __PRETTY_FUNCTION__, *pid);
+        int waitres = waitpid(*pid, &status, 0);
+        if (waitres == -1) {
+            ALOGE("%s: waitpid(%d): %s", __PRETTY_FUNCTION__, *pid, strerror(errno));
+            break;
+        }
+        if (WIFEXITED(status)) {
+            LOG_HOST("%s: PID %d exited.", __PRETTY_FUNCTION__, *pid);
+            pid.reset();
+        } else if (WIFSIGNALED(status)) {
+            LOG_HOST("%s: PID %d terminated by signal %d.", __PRETTY_FUNCTION__, *pid,
+                     WTERMSIG(status));
+            pid.reset();
+        } else if (WIFSTOPPED(status)) {
+            ALOGW("%s: pid %d stopped", __PRETTY_FUNCTION__, *pid);
+        } else if (WIFCONTINUED(status)) {
+            ALOGW("%s: pid %d continued", __PRETTY_FUNCTION__, *pid);
+        }
+    }
+}
+
+std::ostream& operator<<(std::ostream& os, const CommandResult& res) {
+    if (res.exitCode) os << "code=" << *res.exitCode;
+    if (res.signal) os << "signal=" << *res.signal;
+    if (res.pid) os << ", pid=" << *res.pid;
+    return os << ", stdout=" << res.stdout << ", stderr=" << res.stderr;
+}
+
+std::string CommandResult::toString() const {
+    std::stringstream ss;
+    ss << (*this);
+    return ss.str();
+}
+
+android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
+                                             const std::function<bool(const CommandResult&)>& end) {
+    // turn vector<string> into null-terminated char* vector.
+    std::vector<char*> argv;
+    argv.reserve(argStringVec.size() + 1);
+    for (auto& arg : argStringVec) argv.push_back(arg.data());
+    argv.push_back(nullptr);
+
+    CommandResult ret;
+    android::base::unique_fd outWrite;
+    if (!android::base::Pipe(&ret.outPipe, &outWrite))
+        return android::base::ErrnoError() << "pipe() for outPipe";
+    android::base::unique_fd errWrite;
+    if (!android::base::Pipe(&ret.errPipe, &errWrite))
+        return android::base::ErrnoError() << "pipe() for errPipe";
+
+    int pid = fork();
+    if (pid == -1) return android::base::ErrnoError() << "fork()";
+    if (pid == 0) {
+        // child
+        ret.outPipe.reset();
+        ret.errPipe.reset();
+
+        int res = TEMP_FAILURE_RETRY(dup2(outWrite.get(), STDOUT_FILENO));
+        LOG_ALWAYS_FATAL_IF(-1 == res, "dup2(outPipe): %s", strerror(errno));
+        outWrite.reset();
+
+        res = TEMP_FAILURE_RETRY(dup2(errWrite.get(), STDERR_FILENO));
+        LOG_ALWAYS_FATAL_IF(-1 == res, "dup2(errPipe): %s", strerror(errno));
+        errWrite.reset();
+
+        execvp(argv[0], argv.data());
+        LOG_ALWAYS_FATAL("execvp() returns");
+    }
+    // parent
+    outWrite.reset();
+    errWrite.reset();
+    ret.pid = pid;
+
+    auto handlePoll = [](android::base::unique_fd* fd, const pollfd& pfd, std::string* s) {
+        if (!fd->ok()) return true;
+        if (pfd.revents & POLLIN) {
+            char buf[1024];
+            ssize_t n = TEMP_FAILURE_RETRY(read(fd->get(), buf, sizeof(buf)));
+            if (n < 0) return false;
+            if (n > 0) *s += std::string_view(buf, n);
+        }
+        if (pfd.revents & POLLHUP) {
+            fd->reset();
+        }
+        return true;
+    };
+
+    // Drain both stdout and stderr. Check end() regularly until both are closed.
+    while (ret.outPipe.ok() || ret.errPipe.ok()) {
+        pollfd fds[2];
+        pollfd *outPollFd = nullptr, *errPollFd = nullptr;
+        memset(fds, 0, sizeof(fds));
+        nfds_t nfds = 0;
+        if (ret.outPipe.ok()) {
+            outPollFd = &fds[nfds++];
+            *outPollFd = {.fd = ret.outPipe.get(), .events = POLLIN};
+        }
+        if (ret.errPipe.ok()) {
+            errPollFd = &fds[nfds++];
+            *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN};
+        }
+        int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
+        if (pollRet == -1) return android::base::ErrnoError() << "poll()";
+
+        if (!handlePoll(&ret.outPipe, *outPollFd, &ret.stdout))
+            return android::base::ErrnoError() << "read(stdout)";
+        if (!handlePoll(&ret.errPipe, *errPollFd, &ret.stderr))
+            return android::base::ErrnoError() << "read(stderr)";
+
+        if (end && end(ret)) return ret;
+    }
+
+    // If both stdout and stderr are closed by the subprocess, it may or may not be terminated.
+    while (ret.pid.has_value()) {
+        int status;
+        auto exitPid = waitpid(pid, &status, 0);
+        if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")";
+        if (exitPid == pid) {
+            if (WIFEXITED(status)) {
+                ret.pid = std::nullopt;
+                ret.exitCode = WEXITSTATUS(status);
+            } else if (WIFSIGNALED(status)) {
+                ret.pid = std::nullopt;
+                ret.signal = WTERMSIG(status);
+            } else if (WIFSTOPPED(status)) {
+                ALOGW("%s: pid %d stopped", __PRETTY_FUNCTION__, *ret.pid);
+            } else if (WIFCONTINUED(status)) {
+                ALOGW("%s: pid %d continued", __PRETTY_FUNCTION__, *ret.pid);
+            }
+        }
+        // ret is not changed unless the process is terminated (where pid == nullopt). Hence there
+        // is no need to check the predicate `end(ret)`.
+    }
+
+    return ret;
+}
+} // namespace android
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
new file mode 100644
index 0000000..0f29f60
--- /dev/null
+++ b/libs/binder/UtilsHost.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <ostream>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+/**
+ * Log a lot more information about host-device binder communication, when debugging issues.
+ */
+#define SHOULD_LOG_HOST false
+
+#if SHOULD_LOG_HOST
+#define LOG_HOST(...) ALOGI(__VA_ARGS__)
+#else
+#define LOG_HOST(...) ALOGV(__VA_ARGS__) // for type checking
+#endif
+
+namespace android {
+
+struct CommandResult {
+    std::optional<int32_t> exitCode;
+    std::optional<int32_t> signal;
+    std::optional<pid_t> pid;
+    std::string stdout;
+    std::string stderr;
+
+    android::base::unique_fd outPipe;
+    android::base::unique_fd errPipe;
+
+    CommandResult() = default;
+    CommandResult(CommandResult&& other) noexcept { (*this) = std::move(other); }
+    CommandResult& operator=(CommandResult&& other) noexcept {
+        std::swap(exitCode, other.exitCode);
+        std::swap(signal, other.signal);
+        std::swap(pid, other.pid);
+        std::swap(stdout, other.stdout);
+        std::swap(stderr, other.stderr);
+        return *this;
+    }
+    ~CommandResult();
+    [[nodiscard]] std::string toString() const;
+
+    [[nodiscard]] bool stdoutEndsWithNewLine() const {
+        return !stdout.empty() && stdout.back() == '\n';
+    }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(CommandResult);
+};
+
+std::ostream& operator<<(std::ostream& os, const CommandResult& res);
+
+// Execute a command using tokens specified in @a argStringVec.
+//
+// @a end is a predicate checked periodically when the command emits any output to stdout or
+// stderr. When it is evaluated to true, the function returns immediately even though
+// the child process has not been terminated. The function also assumes that, after @a end
+// is evaluated to true, the child process does not emit any other messages.
+// If this is not the case, caller to execute() must handle these I/O in the pipes in the returned
+// CommandResult object. Otherwise the child program may hang on I/O.
+//
+// If @a end is nullptr, it is equivalent to a predicate that always returns false. In this
+// case, execute() returns after the child process is terminated.
+//
+// If @a end is evaluated to true, and execute() returns with the child process running,
+// the returned CommandResult has pid, outPipe, and errPipe set. In this case, the caller is
+// responsible for holding the returned CommandResult. When the CommandResult object is destroyed,
+// the child process is killed.
+//
+// On the other hand, execute() returns with the child process terminated, either exitCode or signal
+// is set.
+//
+// If the parent process has encountered any errors for system calls, return ExecuteError with
+// the proper errno set.
+android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
+                                             const std::function<bool(const CommandResult&)>& end);
+} // namespace android
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 472e546..46223bb 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -54,12 +54,11 @@
                                         uint32_t flags = 0,
                                         wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void        attachObject(   const void* objectID,
-                                        void* object,
-                                        void* cleanupCookie,
-                                        object_cleanup_func func) final;
+    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
+                               object_cleanup_func func) final;
     virtual void*       findObject(const void* objectID) const final;
-    virtual void        detachObject(const void* objectID) final;
+    virtual void* detachObject(const void* objectID) final;
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BBinder*    localBinder();
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 61bf018..c69bb9e 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -72,12 +72,11 @@
                                         uint32_t flags = 0,
                                         wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void        attachObject(   const void* objectID,
-                                        void* object,
-                                        void* cleanupCookie,
-                                        object_cleanup_func func) final;
+    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
+                               object_cleanup_func func) final;
     virtual void*       findObject(const void* objectID) const final;
-    virtual void        detachObject(const void* objectID) final;
+    virtual void* detachObject(const void* objectID) final;
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BpBinder*   remoteBinder();
 
@@ -91,27 +90,23 @@
     static void         setLimitCallback(binder_proxy_limit_callback cb);
     static void         setBinderProxyCountWatermarks(int high, int low);
 
-    class ObjectManager
-    {
+    class ObjectManager {
     public:
-                    ObjectManager();
-                    ~ObjectManager();
+        ObjectManager();
+        ~ObjectManager();
 
-        void        attach( const void* objectID,
-                            void* object,
-                            void* cleanupCookie,
-                            IBinder::object_cleanup_func func);
-        void*       find(const void* objectID) const;
-        void        detach(const void* objectID);
+        void* attach(const void* objectID, void* object, void* cleanupCookie,
+                     IBinder::object_cleanup_func func);
+        void* find(const void* objectID) const;
+        void* detach(const void* objectID);
 
-        void        kill();
+        void kill();
 
     private:
-                    ObjectManager(const ObjectManager&);
+        ObjectManager(const ObjectManager&);
         ObjectManager& operator=(const ObjectManager&);
 
-        struct entry_t
-        {
+        struct entry_t {
             void* object;
             void* cleanupCookie;
             IBinder::object_cleanup_func func;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index f9cdac7..c484d83 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -22,6 +22,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <functional>
+
 // linux/binder.h defines this, but we don't want to include it here in order to
 // avoid exporting the kernel headers
 #ifndef B_PACK_CHARS
@@ -255,26 +257,31 @@
      * objects are invoked with their respective objectID, object, and
      * cleanupCookie. Access to these APIs can be made from multiple threads,
      * but calls from different threads are allowed to be interleaved.
+     *
+     * This returns the object which is already attached. If this returns a
+     * non-null value, it means that attachObject failed. TODO(b/192023359):
+     * remove logs and add [[nodiscard]]
      */
-    virtual void            attachObject(   const void* objectID,
-                                            void* object,
-                                            void* cleanupCookie,
-                                            object_cleanup_func func) = 0;
+    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
+                               object_cleanup_func func) = 0;
     /**
      * Returns object attached with attachObject.
      */
     virtual void*           findObject(const void* objectID) const = 0;
     /**
-     * WARNING: this API does not call the cleanup function for legacy reasons.
-     * It also does not return void* for legacy reasons. If you need to detach
-     * an object and destroy it, there are two options:
-     * - if you can, don't call detachObject and instead wait for the destructor
-     *   to clean it up.
-     * - manually retrieve and destruct the object (if multiple of your threads
-     *   are accessing these APIs, you must guarantee that attachObject isn't
-     *   called after findObject and before detachObject is called).
+     * Returns object attached with attachObject, and detaches it. This does not
+     * delete the object. This is equivalent to using attachObject to attach a null
+     * object.
      */
-    virtual void            detachObject(const void* objectID) = 0;
+    virtual void* detachObject(const void* objectID) = 0;
+
+    /**
+     * Use the lock that this binder contains internally. For instance, this can
+     * be used to modify an attached object without needing to add an additional
+     * lock (though, that attached object must be retrieved before calling this
+     * method). Calling (most) IBinder methods inside this will deadlock.
+     */
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BBinder*        localBinder();
     virtual BpBinder*       remoteBinder();
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 3dbe2c4..8e46147 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -169,4 +169,17 @@
                             int32_t* outPid, int32_t* outUid);
 bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
 
+#ifndef __ANDROID__
+// Create an IServiceManager that delegates the service manager on the device via adb.
+// This is can be set as the default service manager at program start, so that
+// defaultServiceManager() returns it:
+//    int main() {
+//        setDefaultServiceManager(createRpcDelegateServiceManager());
+//        auto sm = defaultServiceManager();
+//        // ...
+//    }
+// Resources are cleaned up when the object is destroyed.
+sp<IServiceManager> createRpcDelegateServiceManager();
+#endif
+
 } // namespace android
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index f3ba830..2e22b84 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -79,9 +79,10 @@
       */
      void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    /**
+     /**
       * Try to unregister all services previously registered with 'registerService'.
-      * Returns 'true' if successful.
+      * Returns 'true' if successful. This should only be called within the callback registered by
+      * setActiveServicesCallback.
       */
      bool tryUnregister();
 
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
new file mode 100644
index 0000000..68ec669
--- /dev/null
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_libbinder.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+using android::RpcServer;
+using android::RpcSession;
+
+extern "C" {
+
+bool RunRpcServer(AIBinder* service, unsigned int port) {
+    auto server = RpcServer::make();
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    if (!server->setupVsockServer(port)) {
+        LOG(ERROR) << "Failed to set up vsock server with port " << port;
+        return false;
+    }
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    server->join();
+
+    // Shutdown any open sessions since server failed.
+    (void)server->shutdown();
+    return true;
+}
+
+AIBinder* RpcClient(unsigned int cid, unsigned int port) {
+    auto session = RpcSession::make();
+    if (!session->setupVsockClient(cid, port)) {
+        LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port;
+        return nullptr;
+    }
+    return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+}
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
new file mode 100644
index 0000000..3921a4d
--- /dev/null
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -0,0 +1,7 @@
+LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
+  global:
+    RunRpcServer;
+    RpcClient;
+  local:
+    *;
+};
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 883403a..266ef37 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -57,7 +57,6 @@
 
 namespace ABpBinderTag {
 
-static std::mutex gLock;
 static const void* kId = "ABpBinder";
 struct Value {
     wp<ABpBinder> binder;
@@ -232,19 +231,14 @@
 ABpBinder::~ABpBinder() {}
 
 void ABpBinder::onLastStrongRef(const void* id) {
-    {
-        std::lock_guard<std::mutex> lock(ABpBinderTag::gLock);
-        // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for
-        // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no
-        // longer be able to exist at the time of this method call, there is no longer a need to
-        // recover it.
+    // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for
+    // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no
+    // longer be able to exist at the time of this method call, there is no longer a need to
+    // recover it.
 
-        ABpBinderTag::Value* value =
-                static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId));
-        if (value != nullptr) {
-            value->binder = nullptr;
-        }
-    }
+    ABpBinderTag::Value* value =
+            static_cast<ABpBinderTag::Value*>(remote()->detachObject(ABpBinderTag::kId));
+    if (value) ABpBinderTag::clean(ABpBinderTag::kId, value, nullptr /*cookie*/);
 
     BpRefBase::onLastStrongRef(id);
 }
@@ -257,23 +251,28 @@
         return static_cast<ABBinder*>(binder.get());
     }
 
-    // The following code ensures that for a given binder object (remote or local), if it is not an
-    // ABBinder then at most one ABpBinder object exists in a given process representing it.
-    std::lock_guard<std::mutex> lock(ABpBinderTag::gLock);
-
-    ABpBinderTag::Value* value =
-            static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId));
+    auto* value = static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId));
     if (value == nullptr) {
         value = new ABpBinderTag::Value;
-        binder->attachObject(ABpBinderTag::kId, static_cast<void*>(value), nullptr /*cookie*/,
-                             ABpBinderTag::clean);
+        auto oldValue = static_cast<ABpBinderTag::Value*>(
+                binder->attachObject(ABpBinderTag::kId, static_cast<void*>(value),
+                                     nullptr /*cookie*/, ABpBinderTag::clean));
+
+        // allocated by another thread
+        if (oldValue) {
+            delete value;
+            value = oldValue;
+        }
     }
 
-    sp<ABpBinder> ret = value->binder.promote();
-    if (ret == nullptr) {
-        ret = new ABpBinder(binder);
-        value->binder = ret;
-    }
+    sp<ABpBinder> ret;
+    binder->withLock([&]() {
+        ret = value->binder.promote();
+        if (ret == nullptr) {
+            ret = sp<ABpBinder>::make(binder);
+            value->binder = ret;
+        }
+    });
 
     return ret;
 }
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 22cacb4..f2c69b3 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -105,6 +105,7 @@
     ABpBinder* asABpBinder() override { return this; }
 
    private:
+    friend android::sp<ABpBinder>;
     explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
 };
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 7d655d8..8d27eed 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -106,6 +106,21 @@
     ],
 }
 
+// TODO(b/184872979): remove once the Rust API is created.
+rust_bindgen {
+    name: "libbinder_rpc_unstable_bindgen",
+    wrapper_src: "src/binder_rpc_unstable.hpp",
+    crate_name: "binder_rpc_unstable_bindgen",
+    source_stem: "bindings",
+    shared_libs: [
+        "libutils",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.virt",
+    ],
+}
+
 rust_test {
     name: "libbinder_rs-internal_test",
     crate_name: "binder",
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 2a09afc..f79b1b7 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -50,7 +50,7 @@
 /// interfaces) must implement this trait.
 ///
 /// This is equivalent `IInterface` in C++.
-pub trait Interface: Send {
+pub trait Interface: Send + Sync {
     /// Convert this binder object into a generic [`SpIBinder`] reference.
     fn as_binder(&self) -> SpIBinder {
         panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
diff --git a/libs/binder/rust/src/binder_rpc_unstable.hpp b/libs/binder/rust/src/binder_rpc_unstable.hpp
new file mode 100644
index 0000000..7932d0f
--- /dev/null
+++ b/libs/binder/rust/src/binder_rpc_unstable.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+extern "C" {
+
+struct AIBinder;
+
+bool RunRpcServer(AIBinder* service, unsigned int port);
+AIBinder* RpcClient(unsigned int cid, unsigned int port);
+
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7c0584b..cb330a6 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -119,6 +119,13 @@
 pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
 pub use state::{ProcessState, ThreadState};
 
+/// Unstable, in-development API that only allowlisted clients are allowed to use.
+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;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 5e324b3..a0dfeec 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -51,6 +51,25 @@
 /// to how `Box<T>` is `Send` if `T` is `Send`.
 unsafe impl<T: Remotable> Send for Binder<T> {}
 
+/// # Safety
+///
+/// A `Binder<T>` is a pair of unique owning pointers to two values:
+///   * a C++ ABBinder which is thread-safe, i.e. `Send + Sync`
+///   * a Rust object which implements `Remotable`; this trait requires `Send + Sync`
+///
+/// `ABBinder` contains an immutable `mUserData` pointer, which is actually a
+/// pointer to a boxed `T: Remotable`, which is `Sync`. `ABBinder` also contains
+/// a mutable pointer to its class, but mutation of this field is controlled by
+/// a mutex and it is only allowed to be set once, therefore we can concurrently
+/// access this field safely. `ABBinder` inherits from `BBinder`, which is also
+/// thread-safe. Thus `ABBinder` is thread-safe.
+///
+/// Both pointers are unique (never escape the `Binder<T>` object and are not copied)
+/// so we can essentially treat `Binder<T>` as a box-like containing the two objects;
+/// the box-like object inherits `Sync` from the two inner values, similarly
+/// to how `Box<T>` is `Sync` if `T` is `Sync`.
+unsafe impl<T: Remotable> Sync for Binder<T> {}
+
 impl<T: Remotable> Binder<T> {
     /// Create a new Binder remotable object with default stability
     ///
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 4a6d118..cdd7c08 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -48,9 +48,14 @@
 
 /// # Safety
 ///
-/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
+/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
 unsafe impl Send for SpIBinder {}
 
+/// # Safety
+///
+/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+unsafe impl Sync for SpIBinder {}
+
 impl SpIBinder {
     /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer.
     ///
@@ -125,6 +130,21 @@
     }
 }
 
+pub mod unstable_api {
+    use super::{sys, SpIBinder};
+
+    /// A temporary API to allow the client to create a `SpIBinder` from a `sys::AIBinder`. This is
+    /// needed to bridge RPC binder, which doesn't have Rust API yet.
+    /// TODO(b/184872979): remove once the Rust API is created.
+    ///
+    /// # Safety
+    ///
+    /// See `SpIBinder::from_raw`.
+    pub unsafe fn new_spibinder(ptr: *mut sys::AIBinder) -> Option<SpIBinder> {
+        SpIBinder::from_raw(ptr)
+    }
+}
+
 /// An object that can be associate with an [`InterfaceClass`].
 pub trait AssociateClass {
     /// Check if this object is a valid object for the given interface class
@@ -433,9 +453,14 @@
 
 /// # Safety
 ///
-/// A `WpIBinder` is a handle to a C++ IBinder, which is thread-safe.
+/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
 unsafe impl Send for WpIBinder {}
 
+/// # Safety
+///
+/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+unsafe impl Sync for WpIBinder {}
+
 impl WpIBinder {
     /// Create a new weak reference from an object that can be converted into a
     /// raw `AIBinder` pointer.
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index af79126..62df9b7 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -23,6 +23,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android/os/BnServiceManager.h>
+#include <android/os/IServiceManager.h>
 #include <binder/IServiceManager.h>
 #include <binder/RpcServer.h>
 
@@ -41,25 +43,38 @@
 using android::base::LogSeverity;
 using android::base::StdioLogger;
 using android::base::StringPrintf;
+using std::string_view_literals::operator""sv;
 
 namespace {
+
+using ServiceRetriever = decltype(&android::IServiceManager::checkService);
+
 int Usage(const char* program) {
+    auto basename = Basename(program);
     auto format = R"(dispatch calls to RPC service.
 Usage:
-  %s <service_name>
+  %s [-g] <service_name>
     <service_name>: the service to connect to.
+  %s [-g] manager
+    Runs an RPC-friendly service that redirects calls to servicemanager.
+
+  -g: use getService() instead of checkService().
+
+  If successful, writes port number and a new line character to stdout, and
+  blocks until killed.
+  Otherwise, writes error message to stderr and exits with non-zero code.
 )";
-    LOG(ERROR) << StringPrintf(format, Basename(program).c_str());
+    LOG(ERROR) << StringPrintf(format, basename.c_str(), basename.c_str());
     return EX_USAGE;
 }
 
-int Dispatch(const char* name) {
+int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) {
     auto sm = defaultServiceManager();
     if (nullptr == sm) {
         LOG(ERROR) << "No servicemanager";
         return EX_SOFTWARE;
     }
-    auto binder = sm->checkService(String16(name));
+    auto binder = std::invoke(serviceRetriever, defaultServiceManager(), String16(name));
     if (nullptr == binder) {
         LOG(ERROR) << "No service \"" << name << "\"";
         return EX_SOFTWARE;
@@ -82,7 +97,7 @@
         LOG(ERROR) << "setRpcClientDebug failed with " << statusToString(status);
         return EX_SOFTWARE;
     }
-    LOG(INFO) << "Finish setting up RPC on service " << name << " on port" << port;
+    LOG(INFO) << "Finish setting up RPC on service " << name << " on port " << port;
 
     std::cout << port << std::endl;
 
@@ -92,6 +107,110 @@
     __builtin_unreachable();
 }
 
+// Wrapper that wraps a BpServiceManager as a BnServiceManager.
+class ServiceManagerProxyToNative : public android::os::BnServiceManager {
+public:
+    ServiceManagerProxyToNative(const sp<android::os::IServiceManager>& impl) : mImpl(impl) {}
+    android::binder::Status getService(const std::string&,
+                                       android::sp<android::IBinder>*) override {
+        // We can't send BpBinder for regular binder over RPC.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status checkService(const std::string&,
+                                         android::sp<android::IBinder>*) override {
+        // We can't send BpBinder for regular binder over RPC.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status addService(const std::string&, const android::sp<android::IBinder>&,
+                                       bool, int32_t) override {
+        // We can't send BpBinder for RPC over regular binder.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status listServices(int32_t dumpPriority,
+                                         std::vector<std::string>* _aidl_return) override {
+        return mImpl->listServices(dumpPriority, _aidl_return);
+    }
+    android::binder::Status registerForNotifications(
+            const std::string&, const android::sp<android::os::IServiceCallback>&) override {
+        // We can't send BpBinder for RPC over regular binder.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status unregisterForNotifications(
+            const std::string&, const android::sp<android::os::IServiceCallback>&) override {
+        // We can't send BpBinder for RPC over regular binder.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status isDeclared(const std::string& name, bool* _aidl_return) override {
+        return mImpl->isDeclared(name, _aidl_return);
+    }
+    android::binder::Status getDeclaredInstances(const std::string& iface,
+                                                 std::vector<std::string>* _aidl_return) override {
+        return mImpl->getDeclaredInstances(iface, _aidl_return);
+    }
+    android::binder::Status updatableViaApex(const std::string& name,
+                                             std::optional<std::string>* _aidl_return) override {
+        return mImpl->updatableViaApex(name, _aidl_return);
+    }
+    android::binder::Status registerClientCallback(
+            const std::string&, const android::sp<android::IBinder>&,
+            const android::sp<android::os::IClientCallback>&) override {
+        // We can't send BpBinder for RPC over regular binder.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status tryUnregisterService(const std::string&,
+                                                 const android::sp<android::IBinder>&) override {
+        // We can't send BpBinder for RPC over regular binder.
+        return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+    }
+    android::binder::Status getServiceDebugInfo(
+            std::vector<android::os::ServiceDebugInfo>* _aidl_return) override {
+        return mImpl->getServiceDebugInfo(_aidl_return);
+    }
+
+private:
+    sp<android::os::IServiceManager> mImpl;
+};
+
+// Workaround for b/191059588.
+// TODO(b/191059588): Once we can run RpcServer on single-threaded services,
+//   `servicedispatcher manager` should call Dispatch("manager") directly.
+int wrapServiceManager(const ServiceRetriever& serviceRetriever) {
+    auto sm = defaultServiceManager();
+    if (nullptr == sm) {
+        LOG(ERROR) << "No servicemanager";
+        return EX_SOFTWARE;
+    }
+    auto service = std::invoke(serviceRetriever, defaultServiceManager(), String16("manager"));
+    if (nullptr == service) {
+        LOG(ERROR) << "No service called `manager`";
+        return EX_SOFTWARE;
+    }
+    auto interface = android::os::IServiceManager::asInterface(service);
+    if (nullptr == interface) {
+        LOG(ERROR) << "Cannot cast service called `manager` to IServiceManager";
+        return EX_SOFTWARE;
+    }
+
+    // Work around restriction that doesn't allow us to send proxy over RPC.
+    interface = sp<ServiceManagerProxyToNative>::make(interface);
+    service = ServiceManagerProxyToNative::asBinder(interface);
+
+    auto rpcServer = RpcServer::make();
+    rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    rpcServer->setRootObject(service);
+    unsigned int port;
+    if (!rpcServer->setupInetServer(0, &port)) {
+        LOG(ERROR) << "Unable to set up inet server";
+        return EX_SOFTWARE;
+    }
+    LOG(INFO) << "Finish wrapping servicemanager with RPC on port " << port;
+    std::cout << port << std::endl;
+    rpcServer->join();
+
+    LOG(FATAL) << "Wrapped servicemanager exits; this should not happen!";
+    __builtin_unreachable();
+}
+
 // Log to logd. For warning and more severe messages, also log to stderr.
 class ServiceDispatcherLogger {
 public:
@@ -120,15 +239,23 @@
     LOG(WARNING) << "WARNING: servicedispatcher is debug only. Use with caution.";
 
     int opt;
-    while (-1 != (opt = getopt(argc, argv, ""))) {
+    ServiceRetriever serviceRetriever = &android::IServiceManager::checkService;
+    while (-1 != (opt = getopt(argc, argv, "g"))) {
         switch (opt) {
+            case 'g': {
+                serviceRetriever = &android::IServiceManager::getService;
+            } break;
             default: {
                 return Usage(argv[0]);
             }
         }
     }
+
     if (optind + 1 != argc) return Usage(argv[0]);
     auto name = argv[optind];
 
-    return Dispatch(name);
+    if (name == "manager"sv) {
+        return wrapServiceManager(serviceRetriever);
+    }
+    return Dispatch(name, serviceRetriever);
 }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 326d9fd..565f88e 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -77,14 +77,14 @@
 
 // unit test only, which can run on host and doesn't use /dev/binder
 cc_test {
-    name: "binderParcelTest",
+    name: "binderUnitTest",
     host_supported: true,
     target: {
         darwin: {
             enabled: false,
         },
     },
-    srcs: ["binderParcelTest.cpp"],
+    srcs: ["binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp"],
     shared_libs: [
         "libbinder",
         "libutils",
@@ -329,3 +329,22 @@
         "libutils",
     ],
 }
+
+cc_test_host {
+    name: "binderUtilsHostTest",
+    defaults: ["binder_test_defaults"],
+    srcs: ["binderUtilsHostTest.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp
new file mode 100644
index 0000000..1be0c59
--- /dev/null
+++ b/libs/binder/tests/binderBinderUnitTest.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <gtest/gtest.h>
+
+using android::BBinder;
+using android::OK;
+using android::sp;
+
+const void* kObjectId1 = reinterpret_cast<const void*>(1);
+const void* kObjectId2 = reinterpret_cast<const void*>(2);
+void* kObject1 = reinterpret_cast<void*>(101);
+void* kObject2 = reinterpret_cast<void*>(102);
+void* kObject3 = reinterpret_cast<void*>(103);
+
+TEST(Binder, AttachObject) {
+    auto binder = sp<BBinder>::make();
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject1, nullptr, nullptr));
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId2, kObject2, nullptr, nullptr));
+    EXPECT_EQ(kObject1, binder->attachObject(kObjectId1, kObject3, nullptr, nullptr));
+}
+
+TEST(Binder, DetachObject) {
+    auto binder = sp<BBinder>::make();
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject1, nullptr, nullptr));
+    EXPECT_EQ(kObject1, binder->detachObject(kObjectId1));
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject2, nullptr, nullptr));
+}
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
similarity index 65%
rename from libs/binder/tests/binderParcelTest.cpp
rename to libs/binder/tests/binderParcelUnitTest.cpp
index 1764228..fb49d7a 100644
--- a/libs/binder/tests/binderParcelTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-#include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
+#include <binder/Parcel.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;
 
 // 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) {
+void parcelOpSameLength(const std::function<void(Parcel*)>& a,
+                        const std::function<void(Parcel*)>& b) {
     Parcel p;
     a(&p);
     size_t end = p.dataPosition();
@@ -38,33 +39,30 @@
 
 TEST(Parcel, InverseInterfaceToken) {
     const String16 token = String16("asdf");
-    parcelOpSameLength([&] (Parcel* p) {
-        p->writeInterfaceToken(token);
-    }, [&] (Parcel* p) {
-        EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
-    });
+    parcelOpSameLength([&](Parcel* p) { p->writeInterfaceToken(token); },
+                       [&](Parcel* p) {
+                           EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+                       });
 }
 
 TEST(Parcel, Utf8FromUtf16Read) {
     const char* token = "asdf";
-    parcelOpSameLength([&] (Parcel* p) {
-        p->writeString16(String16(token));
-    }, [&] (Parcel* p) {
-        std::string s;
-        EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
-        EXPECT_EQ(token, s);
-    });
+    parcelOpSameLength([&](Parcel* p) { p->writeString16(String16(token)); },
+                       [&](Parcel* p) {
+                           std::string s;
+                           EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+                           EXPECT_EQ(token, s);
+                       });
 }
 
 TEST(Parcel, Utf8AsUtf16Write) {
     std::string token = "asdf";
-    parcelOpSameLength([&] (Parcel* p) {
-        p->writeUtf8AsUtf16(token);
-    }, [&] (Parcel* p) {
-        String16 s;
-        EXPECT_EQ(OK, p->readString16(&s));
-        EXPECT_EQ(s, String16(token.c_str()));
-    });
+    parcelOpSameLength([&](Parcel* p) { p->writeUtf8AsUtf16(token); },
+                       [&](Parcel* p) {
+                           String16 s;
+                           EXPECT_EQ(OK, p->readString16(&s));
+                           EXPECT_EQ(s, String16(token.c_str()));
+                       });
 }
 
 template <typename T>
@@ -77,13 +75,12 @@
 template <typename T, typename WRITE_FUNC>
 void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
     for (const T& value : ts) {
-        parcelOpSameLength([&] (Parcel* p) {
-            (*p.*w)(value);
-        }, [&] (Parcel* p) {
-            T outValue;
-            EXPECT_EQ(OK, (*p.*r)(&outValue));
-            EXPECT_EQ(value, outValue);
-        });
+        parcelOpSameLength([&](Parcel* p) { (*p.*w)(value); },
+                           [&](Parcel* p) {
+                               T outValue;
+                               EXPECT_EQ(OK, (*p.*r)(&outValue));
+                               EXPECT_EQ(value, outValue);
+                           });
     }
 }
 
@@ -96,8 +93,8 @@
     readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
 }
 
-#define TEST_READ_WRITE_INVERSE(type, name, ...) \
-    TEST(Parcel, Inverse##name) { \
+#define TEST_READ_WRITE_INVERSE(type, name, ...)                                        \
+    TEST(Parcel, Inverse##name) {                                                       \
         readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
     }
 
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
new file mode 100644
index 0000000..fb24836
--- /dev/null
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 <sysexits.h>
+
+#include <chrono>
+
+#include <android-base/result-gmock.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../UtilsHost.h"
+
+using android::base::testing::Ok;
+using testing::Optional;
+
+namespace android {
+
+TEST(UtilsHost, ExecuteImmediately) {
+    auto result = execute({"echo", "foo"}, nullptr);
+    ASSERT_THAT(result, Ok());
+    EXPECT_THAT(result->exitCode, Optional(EX_OK));
+    EXPECT_EQ(result->stdout, "foo\n");
+}
+
+TEST(UtilsHost, ExecuteLongRunning) {
+    auto now = std::chrono::system_clock::now();
+
+    {
+        std::vector<std::string> args{"sh", "-c",
+                                      "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"};
+        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+            return android::base::EndsWith(commandResult.stdout, "\n");
+        });
+        auto elapsed = std::chrono::system_clock::now() - now;
+        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        EXPECT_GE(elapsedMs, 1000);
+        EXPECT_LT(elapsedMs, 2000);
+
+        ASSERT_THAT(result, Ok());
+        EXPECT_EQ(std::nullopt, result->exitCode);
+        EXPECT_EQ(result->stdout, "foo\n");
+    }
+
+    // ~CommandResult() called, child process is killed.
+    // Assert that the second sleep does not finish.
+    auto elapsed = std::chrono::system_clock::now() - now;
+    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+    EXPECT_LT(elapsedMs, 2000);
+}
+
+TEST(UtilsHost, ExecuteLongRunning2) {
+    auto now = std::chrono::system_clock::now();
+
+    {
+        std::vector<std::string> args{"sh", "-c",
+                                      "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"};
+        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+            return android::base::EndsWith(commandResult.stdout, "\n");
+        });
+        auto elapsed = std::chrono::system_clock::now() - now;
+        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        EXPECT_GE(elapsedMs, 4000);
+        EXPECT_LT(elapsedMs, 6000);
+
+        ASSERT_THAT(result, Ok());
+        EXPECT_EQ(std::nullopt, result->exitCode);
+        EXPECT_EQ(result->stdout, "foo\n");
+    }
+
+    // ~CommandResult() called, child process is killed.
+    // Assert that the second sleep does not finish.
+    auto elapsed = std::chrono::system_clock::now() - now;
+    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+    EXPECT_LT(elapsedMs, 6000);
+}
+
+TEST(UtilsHost, KillWithSigKill) {
+    std::vector<std::string> args{"sh", "-c", "echo foo && sleep 10"};
+    auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+        // FOR TEST PURPOSE ONLY. DON'T DO THIS!
+        if (commandResult.pid.has_value()) {
+            (void)kill(*commandResult.pid, SIGKILL);
+        }
+        // FOR TEST PURPOSE ONLY. DON'T DO THIS!
+        return false;
+    });
+
+    ASSERT_THAT(result, Ok());
+    EXPECT_EQ(std::nullopt, result->exitCode);
+    EXPECT_THAT(result->signal, Optional(SIGKILL));
+}
+
+} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a7cf39a..df308d8 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1621,6 +1621,26 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                                  Rect* outRect, uint32_t* outTransform) {
+    ATRACE_CALL();
+    BQ_LOGV("getLastQueuedBuffer");
+
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+    if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+        *outBuffer = nullptr;
+        *outFence = Fence::NO_FENCE;
+        return NO_ERROR;
+    }
+
+    *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
+    *outFence = mLastQueueBufferFence;
+    *outRect = mLastQueuedCrop;
+    *outTransform = mLastQueuedTransform;
+
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
     addAndGetFrameTimestamps(nullptr, outDelta);
 }
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..6c48534 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,7 @@
     GET_CONSUMER_USAGE,
     SET_LEGACY_BUFFER_DROP,
     SET_AUTO_PREROTATION,
+    GET_LAST_QUEUED_BUFFER2,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -491,6 +492,56 @@
         return result;
     }
 
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER2, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to transact: %d", result);
+            return result;
+        }
+        status_t remoteError = NO_ERROR;
+        result = reply.readInt32(&remoteError);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read status: %d", result);
+            return result;
+        }
+        if (remoteError != NO_ERROR) {
+            return remoteError;
+        }
+        bool hasBuffer = false;
+        result = reply.readBool(&hasBuffer);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+            return result;
+        }
+        sp<GraphicBuffer> buffer;
+        if (hasBuffer) {
+            buffer = new GraphicBuffer();
+            result = reply.read(*buffer);
+            if (result == NO_ERROR) {
+                result = reply.read(*outRect);
+            }
+            if (result == NO_ERROR) {
+                result = reply.readUint32(outTransform);
+            }
+        }
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+            return result;
+        }
+        sp<Fence> fence(new Fence);
+        result = reply.read(*fence);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
+            return result;
+        }
+        *outBuffer = buffer;
+        *outFence = fence;
+        return result;
+    }
+
     virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(
@@ -676,6 +727,11 @@
                 outBuffer, outFence, outTransformMatrix);
     }
 
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect,
+                                 uint32_t* outTransform) override {
+        return mBase->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+    }
+
     void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
         return mBase->getFrameTimestamps(outDelta);
     }
@@ -1025,6 +1081,45 @@
             }
             return NO_ERROR;
         }
+        case GET_LAST_QUEUED_BUFFER2: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            sp<GraphicBuffer> buffer(nullptr);
+            sp<Fence> fence(Fence::NO_FENCE);
+            Rect crop;
+            uint32_t transform;
+            status_t result = getLastQueuedBuffer(&buffer, &fence, &crop, &transform);
+            reply->writeInt32(result);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            if (!buffer.get()) {
+                reply->writeBool(false);
+            } else {
+                reply->writeBool(true);
+                result = reply->write(*buffer);
+                if (result == NO_ERROR) {
+                    result = reply->write(crop);
+                }
+                if (result == NO_ERROR) {
+                    result = reply->writeUint32(transform);
+                }
+            }
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
+                return result;
+            }
+            if (fence == nullptr) {
+                ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+                fence = Fence::NO_FENCE;
+            }
+            result = reply->write(*fence);
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
+                return result;
+            }
+            return NO_ERROR;
+        }
+
         case GET_FRAME_TIMESTAMPS: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             FrameEventHistoryDelta frameTimestamps;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d6f9e63..8a64a4f 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1207,6 +1207,9 @@
     case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
         res = dispatchGetLastQueuedBuffer(args);
         break;
+    case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2:
+        res = dispatchGetLastQueuedBuffer2(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1513,6 +1516,39 @@
     return result;
 }
 
+int Surface::dispatchGetLastQueuedBuffer2(va_list args) {
+    AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+    int* fence = va_arg(args, int*);
+    ARect* crop = va_arg(args, ARect*);
+    uint32_t* transform = va_arg(args, uint32_t*);
+    sp<GraphicBuffer> graphicBuffer;
+    sp<Fence> spFence;
+
+    Rect r;
+    int result =
+            mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, &r, transform);
+
+    if (graphicBuffer != nullptr) {
+        *buffer = graphicBuffer->toAHardwareBuffer();
+        AHardwareBuffer_acquire(*buffer);
+
+        // Avoid setting crop* unless buffer is valid (matches IGBP behavior)
+        crop->left = r.left;
+        crop->top = r.top;
+        crop->right = r.right;
+        crop->bottom = r.bottom;
+    } else {
+        *buffer = nullptr;
+    }
+
+    if (spFence != nullptr) {
+        *fence = spFence->dup();
+    } else {
+        *fence = -1;
+    }
+    return result;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index a7f7d1d..0ad3075 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -186,6 +186,10 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
 
+    // See IGraphicBufferProducer::getLastQueuedBuffer
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override;
+
     // See IGraphicBufferProducer::getFrameTimestamps
     virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
 
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..4905b74 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -619,6 +619,22 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
 
+    // Returns the last queued buffer along with a fence which must signal
+    // before the contents of the buffer are read. If there are no buffers in
+    // the queue, outBuffer will be populated with nullptr and outFence will be
+    // populated with Fence::NO_FENCE
+    //
+    // outRect & outTransform are not modified if outBuffer is null.
+    //
+    // Returns NO_ERROR or the status of the Binder transaction
+    virtual status_t getLastQueuedBuffer([[maybe_unused]] sp<GraphicBuffer>* outBuffer,
+                                         [[maybe_unused]] sp<Fence>* outFence,
+                                         [[maybe_unused]] Rect* outRect,
+                                         [[maybe_unused]] uint32_t* outTransform) {
+        // Too many things implement IGraphicBufferProducer...
+        return UNKNOWN_TRANSACTION;
+    }
+
     // Gets the frame events that haven't already been retrieved.
     virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
 
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 55b4101..10f4df9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -265,6 +265,7 @@
     int dispatchAddQueueInterceptor(va_list args);
     int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
+    int dispatchGetLastQueuedBuffer2(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b78fc5d..73b46b2 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -255,6 +255,7 @@
     NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
+    NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2         = 50,    /* private */
     // clang-format on
 };
 
@@ -1053,6 +1054,31 @@
 }
 
 /**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read. The cropRect & transform should be applied to the
+ * buffer's content.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ * \return STATUS_UNKNOWN_TRANSACTION if this ANativeWindow doesn't support this method, callers
+ *         should fall back to ANativeWindow_getLastQueuedBuffer instead.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window,
+                                                     AHardwareBuffer** outBuffer, int* outFence,
+                                                     ARect* outCropRect, uint32_t* outTransform) {
+    return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2, outBuffer, outFence,
+                           outCropRect, outTransform);
+}
+
+/**
  * Retrieves an identifier for the next frame to be queued by this window.
  *
  * \return the next frame id.
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 18a2891..6b2d745 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -140,6 +140,11 @@
             outTransformMatrix);
 }
 
+status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                                Rect* outRect, uint32_t* outTransform) {
+    return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+}
+
 void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
     mProducer->getFrameTimestamps(outDelta);
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 788919b..3778277 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -64,6 +64,8 @@
     virtual status_t setLegacyBufferDrop(bool drop) override;
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override;
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;