Merge "sf-caching: Skip hole punch for layer that blends" into tm-dev
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7bde21..77bed0e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -41,6 +41,7 @@
 #include <fstream>
 #include <functional>
 #include <regex>
+#include <unordered_set>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -81,6 +82,7 @@
 #define GRANULAR_LOCKS
 
 using android::base::ParseUint;
+using android::base::Split;
 using android::base::StringPrintf;
 using std::endl;
 
@@ -456,8 +458,8 @@
     return res;
 }
 
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
-    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid) {
+    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
         PLOG(ERROR) << "Failed to prepare " << path;
         return -1;
     }
@@ -597,9 +599,9 @@
     }
 }
 
-static binder::Status createAppDataDirs(const std::string& path,
-        int32_t uid, int32_t* previousUid, int32_t cacheGid,
-        const std::string& seInfo, mode_t targetMode) {
+static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid,
+                                        int32_t* previousUid, int32_t cacheGid,
+                                        const std::string& seInfo, mode_t targetMode) {
     struct stat st{};
     bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
 
@@ -623,9 +625,9 @@
     }
 
     // Prepare only the parent app directory
-    if (prepare_app_dir(path, targetMode, uid) ||
-            prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
-            prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+    if (prepare_app_dir(path, targetMode, uid, gid) ||
+        prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+        prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
         return error("Failed to prepare " + path);
     }
 
@@ -684,7 +686,7 @@
     if (flags & FLAG_STORAGE_CE) {
         auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
 
-        auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+        auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
         if (!status.isOk()) {
             return status;
         }
@@ -709,7 +711,7 @@
     if (flags & FLAG_STORAGE_DE) {
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
 
-        auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+        auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
         if (!status.isOk()) {
             return status;
         }
@@ -722,72 +724,77 @@
         }
     }
 
-    // TODO(b/220095381): Due to boot time regression, we have omitted call to
-    // createAppDirectoryForSupplementalData from here temporarily (unless it's for testing)
-    if (uuid_ != nullptr && strcmp(uuid_, "TEST") == 0) {
-        auto status = createAppDirectoryForSupplementalData(uuid, packageName, userId, appId,
-                                                            previousAppId, seInfo, flags);
-        if (!status.isOk()) {
-            return status;
+    if (flags & FLAG_STORAGE_SDK) {
+        // Safe to ignore status since we can retry creating this by calling reconcileSdkData
+        auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+                                                           previousAppId, seInfo, flags);
+        if (!ignore.isOk()) {
+            PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
         }
+
+    } else {
+        // Package does not need sdk storage. Remove it.
+        destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
     }
 
     return ok();
 }
 
 /**
- * Responsible for creating /data/user/0/supplemental/<app-name> directory and other
+ * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory and other
  * app level sub directories, such as ./shared
  */
-binder::Status InstalldNativeService::createAppDirectoryForSupplementalData(
+binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) {
-    int32_t supplementalUid = multiuser_get_supplemental_uid(userId, appId);
-    if (supplementalUid == -1) {
-        // There no valid supplemental process for this app. Skip creation of data directory
+    int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+    if (sdkSandboxUid == -1) {
+        // There no valid sdk sandbox process for this app. Skip creation of data directory
         return ok();
     }
 
-    // TODO(b/211763739): what if uuid is not nullptr or TEST?
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
     constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
-    for (int i = 0; i < 2; i++) {
-        int currentFlag = storageFlags[i];
+    for (int currentFlag : storageFlags) {
         if ((flags & currentFlag) == 0) {
             continue;
         }
         bool isCeData = (currentFlag == FLAG_STORAGE_CE);
 
-        // /data/misc_{ce,de}/<user-id>/supplemental directory gets created by vold
+        // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold
         // during user creation
 
         // Prepare the app directory
-        auto appPath = create_data_misc_supplemental_package_path(uuid_, isCeData, userId,
-                                                                  packageName.c_str());
-        if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
-            return error("Failed to prepare " + appPath);
+        auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+                                                                     packageName.c_str());
+#if SDK_DEBUG
+        LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath;
+#endif
+
+        if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM)) {
+            return error("Failed to prepare " + packagePath);
         }
 
         // Now prepare the shared directory which will be accessible by all codes
-        auto sharedPath = create_data_misc_supplemental_shared_path(uuid_, isCeData, userId,
-                                                                    packageName.c_str());
+        auto sharedPath = create_data_misc_sdk_sandbox_shared_path(uuid_, isCeData, userId,
+                                                                   packageName.c_str());
 
-        int32_t previousSupplementalUid = multiuser_get_supplemental_uid(userId, previousAppId);
+        int32_t previousSdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
         int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
         if (cacheGid == -1) {
             return exception(binder::Status::EX_ILLEGAL_STATE,
-                             StringPrintf("cacheGid cannot be -1 for supplemental data"));
+                             StringPrintf("cacheGid cannot be -1 for sdksandbox data"));
         }
-        auto status = createAppDataDirs(sharedPath, supplementalUid, &previousSupplementalUid,
-                                        cacheGid, seInfo, 0700);
+        auto status = createAppDataDirs(sharedPath, sdkSandboxUid, AID_NOBODY,
+                                        &previousSdkSandboxUid, cacheGid, seInfo, 0700);
         if (!status.isOk()) {
             return status;
         }
 
         // TODO(b/211763739): We also need to handle art profile creations
 
-        // TODO(b/211763739): And return the CE inode of the supplemental root directory and
+        // TODO(b/211763739): And return the CE inode of the sdksandbox root directory and
         // app directory under it so we can clear contents while CE storage is locked
     }
 
@@ -837,6 +844,140 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::reconcileSdkData(
+        const android::os::ReconcileSdkDataArgs& args) {
+    // Locking is performed depeer in the callstack.
+
+    return reconcileSdkData(args.uuid, args.packageName, args.sdkPackageNames, args.randomSuffixes,
+                            args.userId, args.appId, args.previousAppId, args.seInfo, args.flags);
+}
+
+/**
+ * Reconciles per-sdk directory under app-level sdk data directory.
+
+ * E.g. `/data/misc_ce/0/sdksandbox/<package-name>/<sdkPackageName>-<randomSuffix>
+ *
+ * - If the sdk data package directory is missing, we create it first.
+ * - If sdkPackageNames is empty, we delete sdk package directory since it's not needed anymore.
+ * - If a sdk level directory we need to prepare already exist, we skip creating it again. This
+ *   is to avoid having same per-sdk directory with different suffix.
+ * - If a sdk level directory exist which is absent from sdkPackageNames, we remove it.
+ */
+binder::Status InstalldNativeService::reconcileSdkData(
+        const std::optional<std::string>& uuid, const std::string& packageName,
+        const std::vector<std::string>& sdkPackageNames,
+        const std::vector<std::string>& randomSuffixes, int userId, int appId, int previousAppId,
+        const std::string& seInfo, int flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    for (const auto& sdkPackageName : sdkPackageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(sdkPackageName);
+    }
+    LOCK_PACKAGE_USER();
+
+#if SDK_DEBUG
+    LOG(DEBUG) << "Creating per sdk data directory for: " << packageName;
+#endif
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    // Validate we have enough randomSuffixStrings
+    if (randomSuffixes.size() != sdkPackageNames.size()) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                         StringPrintf("Not enough random suffix. Required %d, received %d.",
+                                      (int)sdkPackageNames.size(), (int)randomSuffixes.size()));
+    }
+
+    // Prepare the sdk package directory in case it's missing
+    const auto status = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+                                                             previousAppId, seInfo, flags);
+    if (!status.isOk()) {
+        return status;
+    }
+
+    auto res = ok();
+    // We have to create sdk data for CE and DE storage
+    const int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int currentFlag : storageFlags) {
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        const bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+        // Since random suffix provided will be random every time, we need to ensure we don't end up
+        // creating multuple directories for same sdk package with different suffixes. This
+        // is ensured by fetching all the existing sub directories and storing them so that we can
+        // check for existence later. We also remove unconsumed sdk directories in this check.
+        const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+                                                                           packageName.c_str());
+        const std::unordered_set<std::string> expectedSdkNames(sdkPackageNames.begin(),
+                                                               sdkPackageNames.end());
+        // Store paths of per-sdk directory for sdk that already exists
+        std::unordered_map<std::string, std::string> sdkNamesThatExist;
+
+        const auto subDirHandler = [&packagePath, &expectedSdkNames, &sdkNamesThatExist,
+                                    &res](const std::string& filename) {
+            auto filepath = packagePath + "/" + filename;
+            auto tokens = Split(filename, "@");
+            if (tokens.size() != 2) {
+                // Not a per-sdk directory with random suffix
+                return;
+            }
+            auto sdkName = tokens[0];
+
+            // Remove the per-sdk directory if it is not referred in
+            // expectedSdkNames
+            if (expectedSdkNames.find(sdkName) == expectedSdkNames.end()) {
+                if (delete_dir_contents_and_dir(filepath) != 0) {
+                    res = error("Failed to delete " + filepath);
+                    return;
+                }
+            } else {
+                // Otherwise, store it as existing sdk level directory
+                sdkNamesThatExist[sdkName] = filepath;
+            }
+        };
+        const int ec = foreach_subdir(packagePath, subDirHandler);
+        if (ec != 0) {
+            res = error("Failed to process subdirs for " + packagePath);
+            continue;
+        }
+
+        // Create sdksandbox data directory for each sdksandbox package
+        for (int i = 0, size = sdkPackageNames.size(); i < size; i++) {
+            const std::string& sdkName = sdkPackageNames[i];
+            const std::string& randomSuffix = randomSuffixes[i];
+            std::string path;
+            if (const auto& it = sdkNamesThatExist.find(sdkName); it != sdkNamesThatExist.end()) {
+                // Already exists. Use existing path instead of creating a new one
+                path = it->second;
+            } else {
+                path = create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId,
+                                                             packageName.c_str(), sdkName.c_str(),
+                                                             randomSuffix.c_str());
+            }
+
+            // Create the directory along with cache and code_cache
+            const int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+            if (cacheGid == -1) {
+                return exception(binder::Status::EX_ILLEGAL_STATE,
+                                 StringPrintf("cacheGid cannot be -1 for sdk data"));
+            }
+            const int32_t sandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+            int32_t previousSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
+            auto status = createAppDataDirs(path, sandboxUid, AID_NOBODY, &previousSandboxUid,
+                                            cacheGid, seInfo, 0700);
+            if (!status.isOk()) {
+                res = status;
+                continue;
+            }
+        }
+    }
+
+    return res;
+}
+
 binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
@@ -982,6 +1123,47 @@
             }
         }
     }
+    auto status = clearSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+    if (!status.isOk()) {
+        res = status;
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+        std::string suffix;
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            suffix = CACHE_DIR_POSTFIX;
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            suffix = CODE_CACHE_DIR_POSTFIX;
+        }
+
+        auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+        if (access(appPath.c_str(), F_OK) != 0) continue;
+        const auto subDirHandler = [&appPath, &res, &suffix](const std::string& filename) {
+            auto filepath = appPath + "/" + filename + suffix;
+            if (delete_dir_contents(filepath, true) != 0) {
+                res = error("Failed to clear contents of " + filepath);
+            }
+        };
+        const int ec = foreach_subdir(appPath, subDirHandler);
+        if (ec != 0) {
+            res = error("Failed to process subdirs for " + appPath);
+        }
+    }
     return res;
 }
 
@@ -1078,6 +1260,32 @@
             }
         }
     }
+    auto status = destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+    if (!status.isOk()) {
+        res = status;
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+        auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+        if (rename_delete_dir_contents_and_dir(appPath) != 0) {
+            res = error("Failed to delete " + appPath);
+        }
+    }
     return res;
 }
 
@@ -1563,6 +1771,36 @@
         }
     }
 
+    // Copy sdk data for all known users
+    for (auto userId : users) {
+        LOCK_USER();
+
+        constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+        for (int currentFlag : storageFlags) {
+            const bool isCeData = currentFlag == FLAG_STORAGE_CE;
+
+            const auto from = create_data_misc_sdk_sandbox_package_path(from_uuid, isCeData, userId,
+                                                                        package_name);
+            if (access(from.c_str(), F_OK) != 0) {
+                LOG(INFO) << "Missing source " << from;
+                continue;
+            }
+            const auto to = create_data_misc_sdk_sandbox_path(to_uuid, isCeData, userId);
+
+            const int rc = copy_directory_recursive(from.c_str(), to.c_str());
+            if (rc != 0) {
+                res = error(rc, "Failed copying " + from + " to " + to);
+                goto fail;
+            }
+        }
+
+        if (!restoreconSdkDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+                                     appId, seInfo)
+                     .isOk()) {
+            res = error("Failed to restorecon");
+            goto fail;
+        }
+    }
     // We let the framework scan the new location and persist that before
     // deleting the data in the old location; this ordering ensures that
     // we can recover from things like battery pulls.
@@ -1590,6 +1828,18 @@
             }
         }
     }
+    for (auto userId : users) {
+        LOCK_USER();
+        constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+        for (int currentFlag : storageFlags) {
+            const bool isCeData = currentFlag == FLAG_STORAGE_CE;
+            const auto to = create_data_misc_sdk_sandbox_package_path(to_uuid, isCeData, userId,
+                                                                      package_name);
+            if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+    }
     return res;
 }
 
@@ -1624,6 +1874,11 @@
         if (delete_dir_contents_and_dir(path, true) != 0) {
             res = error("Failed to delete " + path);
         }
+        auto sdk_sandbox_de_path =
+                create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId);
+        if (delete_dir_contents_and_dir(sdk_sandbox_de_path, true) != 0) {
+            res = error("Failed to delete " + sdk_sandbox_de_path);
+        }
         if (uuid_ == nullptr) {
             path = create_data_misc_legacy_path(userId);
             if (delete_dir_contents_and_dir(path, true) != 0) {
@@ -1640,6 +1895,11 @@
         if (delete_dir_contents_and_dir(path, true) != 0) {
             res = error("Failed to delete " + path);
         }
+        auto sdk_sandbox_ce_path =
+                create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId);
+        if (delete_dir_contents_and_dir(sdk_sandbox_ce_path, true) != 0) {
+            res = error("Failed to delete " + sdk_sandbox_ce_path);
+        }
         path = findDataMediaPath(uuid, userId);
         if (delete_dir_contents_and_dir(path, true) != 0) {
             res = error("Failed to delete " + path);
@@ -2084,6 +2344,10 @@
         const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
+    if (packageNames.size() != ceDataInodes.size()) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                         "packageNames/ceDataInodes size mismatch.");
+    }
     for (const auto& packageName : packageNames) {
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     }
@@ -2918,6 +3182,49 @@
     return res;
 }
 
+binder::Status InstalldNativeService::restoreconSdkDataLocked(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags, int32_t appId, const std::string& seInfo) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+    binder::Status res = ok();
+
+    // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+    unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgName = packageName.c_str();
+    const char* seinfo = seInfo.c_str();
+
+    uid_t uid = multiuser_get_sdk_sandbox_uid(userId, appId);
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int currentFlag : storageFlags) {
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        const bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+        const auto packagePath =
+                create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgName);
+        if (access(packagePath.c_str(), F_OK) != 0) {
+            LOG(INFO) << "Missing source " << packagePath;
+            continue;
+        }
+        const auto subDirHandler = [&packagePath, &seinfo, &uid, &seflags,
+                                    &res](const std::string& subDir) {
+            const auto& fullpath = packagePath + "/" + subDir;
+            if (selinux_android_restorecon_pkgdir(fullpath.c_str(), seinfo, uid, seflags) < 0) {
+                res = error("restorecon failed for " + fullpath);
+            }
+        };
+        const auto ec = foreach_subdir(packagePath, subDirHandler);
+        if (ec != 0) {
+            res = error("Failed to restorecon for subdirs of " + packagePath);
+        }
+    }
+    return res;
+}
+
 binder::Status InstalldNativeService::createOatDir(const std::string& packageName,
                                                    const std::string& oatDir,
                                                    const std::string& instructionSet) {
@@ -3267,11 +3574,17 @@
     if (flags & FLAG_STORAGE_CE) {
         auto ce_path = create_data_user_ce_path(uuid_cstr, userId);
         cleanup_invalid_package_dirs_under_path(ce_path);
+        auto sdksandbox_ce_path =
+                create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/true, userId);
+        cleanup_invalid_package_dirs_under_path(sdksandbox_ce_path);
     }
 
     if (flags & FLAG_STORAGE_DE) {
         auto de_path = create_data_user_de_path(uuid_cstr, userId);
         cleanup_invalid_package_dirs_under_path(de_path);
+        auto sdksandbox_de_path =
+                create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/false, userId);
+        cleanup_invalid_package_dirs_under_path(sdksandbox_de_path);
     }
 
     return ok();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 6b4ba1f..1f0fc9c 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -58,12 +58,12 @@
             const std::vector<android::os::CreateAppDataArgs>& args,
             std::vector<android::os::CreateAppDataResult>* _aidl_return);
 
+    binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args);
+
     binder::Status restoreconAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             const std::string& seInfo);
-    binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
-                                           const std::string& packageName, int32_t userId,
-                                           int32_t flags, int32_t appId, const std::string& seInfo);
+
     binder::Status migrateAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags);
     binder::Status clearAppData(const std::optional<std::string>& uuid,
@@ -204,12 +204,30 @@
                                        int32_t flags, int32_t appId, int32_t previousAppId,
                                        const std::string& seInfo, int32_t targetSdkVersion,
                                        int64_t* _aidl_return);
+    binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
+                                           const std::string& packageName, int32_t userId,
+                                           int32_t flags, int32_t appId, const std::string& seInfo);
 
-    binder::Status createAppDirectoryForSupplementalData(const std::optional<std::string>& uuid,
+    binder::Status createSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
+                                                        const std::string& packageName,
+                                                        int32_t userId, int32_t appId,
+                                                        int32_t previousAppId,
+                                                        const std::string& seInfo, int32_t flags);
+    binder::Status clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
+                                                       const std::string& packageName,
+                                                       int32_t userId, int32_t flags);
+    binder::Status destroySdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
                                                          const std::string& packageName,
-                                                         int32_t userId, int32_t appId,
-                                                         int32_t previousAppId,
-                                                         const std::string& seInfo, int32_t flags);
+                                                         int32_t userId, int32_t flags);
+    binder::Status reconcileSdkData(const std::optional<std::string>& uuid,
+                                    const std::string& packageName,
+                                    const std::vector<std::string>& sdkPackageNames,
+                                    const std::vector<std::string>& randomSuffixes, int32_t userId,
+                                    int32_t appId, int32_t previousAppId, const std::string& seInfo,
+                                    int flags);
+    binder::Status restoreconSdkDataLocked(const std::optional<std::string>& uuid,
+                                           const std::string& packageName, int32_t userId,
+                                           int32_t flags, int32_t appId, const std::string& seInfo);
 };
 
 }  // namespace installd
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index afedcc6..e08e9b6 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -24,6 +24,8 @@
     android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
     android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
 
+    void reconcileSdkData(in android.os.ReconcileSdkDataArgs args);
+
     void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, int flags, int appId, @utf8InCpp String seInfo);
     void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
@@ -131,6 +133,7 @@
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
     const int FLAG_STORAGE_EXTERNAL = 0x4;
+    const int FLAG_STORAGE_SDK = 0x8;
 
     const int FLAG_CLEAR_CACHE_ONLY = 0x10;
     const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20;
diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
new file mode 100644
index 0000000..2f794b1
--- /dev/null
+++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable ReconcileSdkDataArgs {
+    @nullable @utf8InCpp String uuid;
+    @utf8InCpp String packageName;
+    @utf8InCpp List<String> sdkPackageNames;
+    @utf8InCpp List<String> randomSuffixes;
+    int userId;
+    int appId;
+    int previousAppId;
+    @utf8InCpp String seInfo;
+    int flags;
+}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index c796da6..9647865 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -74,10 +74,19 @@
 using android::base::ReadFully;
 using android::base::StringPrintf;
 using android::base::WriteFully;
+using android::base::borrowed_fd;
 using android::base::unique_fd;
 
 namespace {
 
+// Timeout for short operations, such as merging profiles.
+constexpr int kShortTimeoutMs = 60000; // 1 minute.
+
+// Timeout for long operations, such as compilation. This should be smaller than the Package Manager
+// watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that the operation will be
+// aborted before that watchdog would take down the system server.
+constexpr int kLongTimeoutMs = 570000; // 9.5 minutes.
+
 class DexOptStatus {
  public:
     // Check if dexopt is cancelled and fork if it is not cancelled.
@@ -486,6 +495,25 @@
     }
 }
 
+// Cleans up an output file specified by a file descriptor. This function should be called whenever
+// a subprocess that modifies a system-managed file crashes.
+// If the subprocess crashes while it's writing to the file, the file is likely corrupted, so we
+// should remove it.
+// If the subprocess times out and is killed while it's acquiring a flock on the file, there is
+// probably a deadlock, so it's also good to remove the file so that later operations won't
+// encounter the same problem. It's safe to do so because the process that is holding the flock will
+// still have access to the file until the file descriptor is closed.
+// Note that we can't do `clear_reference_profile` here even if the fd points to a reference profile
+// because that also requires a flock and is therefore likely to be stuck in the second case.
+static bool cleanup_output_fd(int fd) {
+    std::string path;
+    bool ret = remove_file_at_fd(fd, &path);
+    if (ret) {
+        LOG(INFO) << "Removed file at path " << path;
+    }
+    return ret;
+}
+
 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_NOT_ENOUGH_DELTA = 2;
@@ -497,13 +525,14 @@
 
 class RunProfman : public ExecVHelper {
   public:
-   void SetupArgs(const std::vector<unique_fd>& profile_fds,
-                  const unique_fd& reference_profile_fd,
-                  const std::vector<unique_fd>& apk_fds,
-                  const std::vector<std::string>& dex_locations,
-                  bool copy_and_update,
-                  bool for_snapshot,
-                  bool for_boot_image) {
+    template <typename T, typename U>
+    void SetupArgs(const std::vector<T>& profile_fds,
+                   const unique_fd& reference_profile_fd,
+                   const std::vector<U>& apk_fds,
+                   const std::vector<std::string>& dex_locations,
+                   bool copy_and_update,
+                   bool for_snapshot,
+                   bool for_boot_image) {
 
         // TODO(calin): Assume for now we run in the bg compile job (which is in
         // most of the invocation). With the current data flow, is not very easy or
@@ -519,11 +548,11 @@
             AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get()));
         }
 
-        for (const unique_fd& fd : profile_fds) {
+        for (const T& fd : profile_fds) {
             AddArg("--profile-file-fd=" + std::to_string(fd.get()));
         }
 
-        for (const unique_fd& fd : apk_fds) {
+        for (const U& fd : apk_fds) {
             AddArg("--apk-fd=" + std::to_string(fd.get()));
         }
 
@@ -582,20 +611,14 @@
                   for_boot_image);
     }
 
-    void SetupCopyAndUpdate(unique_fd&& profile_fd,
-                            unique_fd&& reference_profile_fd,
-                            unique_fd&& apk_fd,
+    void SetupCopyAndUpdate(const unique_fd& profile_fd,
+                            const unique_fd& reference_profile_fd,
+                            const unique_fd& apk_fd,
                             const std::string& dex_location) {
-        // The fds need to stay open longer than the scope of the function, so put them into a local
-        // variable vector.
-        profiles_fd_.push_back(std::move(profile_fd));
-        apk_fds_.push_back(std::move(apk_fd));
-        reference_profile_fd_ = std::move(reference_profile_fd);
-        std::vector<std::string> dex_locations = {dex_location};
-        SetupArgs(profiles_fd_,
-                  reference_profile_fd_,
-                  apk_fds_,
-                  dex_locations,
+        SetupArgs(std::vector<borrowed_fd>{profile_fd},
+                  reference_profile_fd,
+                  std::vector<borrowed_fd>{apk_fd},
+                  {dex_location},
                   /*copy_and_update=*/true,
                   /*for_snapshot*/false,
                   /*for_boot_image*/false);
@@ -621,11 +644,6 @@
     void Exec() {
         ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
     }
-
-  private:
-    unique_fd reference_profile_fd_;
-    std::vector<unique_fd> profiles_fd_;
-    std::vector<unique_fd> apk_fds_;
 };
 
 static int analyze_profiles(uid_t uid, const std::string& package_name,
@@ -657,13 +675,14 @@
         profman_merge.Exec();
     }
     /* parent */
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     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)) {
         LOG(WARNING) << "profman failed for location " << location << ": " << return_code;
+        cleanup_output_fd(reference_profile_fd.get());
     } else {
         return_code = WEXITSTATUS(return_code);
         switch (return_code) {
@@ -797,10 +816,10 @@
         profman_dump.Exec();
     }
     /* parent */
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for package " << pkgname << ": "
-                << return_code;
+        LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
+        cleanup_output_fd(output_fd.get());
         return false;
     }
     return true;
@@ -871,7 +890,11 @@
         _exit(0);
     }
     /* parent */
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
+    if (!WIFEXITED(return_code)) {
+        cleanup_output_fd(out_fd.get());
+        return false;
+    }
     return return_code == 0;
 }
 
@@ -1521,7 +1544,7 @@
     }
     pipe_read.reset();
 
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     if (!WIFEXITED(return_code)) {
         PLOG(ERROR) << "Error waiting for child dexoptanalyzer process";
         return false;
@@ -1695,7 +1718,7 @@
     }
 
     /* parent */
-    int result = wait_child(pid);
+    int result = wait_child_with_timeout(pid, kShortTimeoutMs);
     cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
     if (!WIFEXITED(result)) {
         if ((WTERMSIG(result) == SIGKILL) && cancelled) {
@@ -1954,7 +1977,7 @@
 
         runner.Exec(DexoptReturnCodes::kDex2oatExec);
     } else {
-        int res = wait_child(pid);
+        int res = wait_child_with_timeout(pid, kLongTimeoutMs);
         bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
         if (res == 0) {
             LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---";
@@ -2143,7 +2166,7 @@
         _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
     }
 
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     if (!WIFEXITED(return_code)) {
         LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code;
     } else {
@@ -2261,7 +2284,7 @@
     if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) {
         out_secondary_dex_hash->clear();
     }
-    return wait_child(pid) == 0;
+    return wait_child_with_timeout(pid, kShortTimeoutMs) == 0;
 }
 
 // Helper for move_ab, so that we can have common failure-case cleanup.
@@ -2591,9 +2614,10 @@
     }
 
     /* parent */
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     if (!WIFEXITED(return_code)) {
         LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+        cleanup_output_fd(snapshot_fd.get());
         return false;
     }
 
@@ -2700,10 +2724,11 @@
         }
 
         /* parent */
-        int return_code = wait_child(pid);
+        int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
 
         if (!WIFEXITED(return_code)) {
             PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+            cleanup_output_fd(snapshot_fd.get());
             return false;
         }
 
@@ -2774,9 +2799,9 @@
     }
 
     RunProfman args;
-    args.SetupCopyAndUpdate(std::move(dex_metadata_fd),
-                            std::move(ref_profile_fd),
-                            std::move(apk_fd),
+    args.SetupCopyAndUpdate(dex_metadata_fd,
+                            ref_profile_fd,
+                            apk_fd,
                             code_path);
     pid_t pid = fork();
     if (pid == 0) {
@@ -2789,9 +2814,10 @@
     }
 
     /* parent */
-    int return_code = wait_child(pid);
+    int return_code = wait_child_with_timeout(pid, kShortTimeoutMs);
     if (!WIFEXITED(return_code)) {
         PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+        cleanup_output_fd(ref_profile_fd.get());
         return false;
     }
     return true;
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index e390bab..b3baca5 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -26,6 +26,7 @@
         "libasync_safe",
         "libdiskusage",
         "libext2_uuid",
+        "libgmock",
         "libinstalld",
         "liblog",
     ],
@@ -106,6 +107,7 @@
         "libziparchive",
         "liblog",
         "liblogwrap",
+        "libc++fs",
     ],
     test_config: "installd_service_test.xml",
 
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index bb36c39..f21a304 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -50,6 +50,8 @@
 namespace android {
 namespace installd {
 
+constexpr int kTimeoutMs = 60000;
+
 // TODO(calin): try to dedup this code.
 #if defined(__arm__)
 static const std::string kRuntimeIsa = "arm";
@@ -1073,7 +1075,7 @@
             _exit(0);
         }
         /* parent */
-        ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+        ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs)));
     }
 
     void mergePackageProfiles(const std::string& package_name,
@@ -1377,7 +1379,7 @@
             _exit(0);
         }
         /* parent */
-        ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+        ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs)));
     }
   protected:
     std::string intial_android_profiles_dir;
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 703a096..04558d5 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -32,16 +32,20 @@
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
 
 #include <android/content/pm/IPackageManagerNative.h>
 #include <binder/IServiceManager.h>
 #include "InstalldNativeService.h"
+#include "binder/Status.h"
 #include "binder_test_utils.h"
 #include "dexopt.h"
 #include "globals.h"
 #include "utils.h"
 
 using android::base::StringPrintf;
+namespace fs = std::filesystem;
 
 namespace android {
 std::string get_package_name(uid_t uid) {
@@ -75,13 +79,16 @@
 namespace installd {
 
 static constexpr const char* kTestUuid = "TEST";
-static constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const char* kTestPath = "/data/local/tmp";
+static constexpr const uid_t kNobodyUid = 9999;
 static constexpr const uid_t kSystemUid = 1000;
 static constexpr const int32_t kTestUserId = 0;
 static constexpr const uid_t kTestAppId = 19999;
+static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK;
 
 const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
-const uid_t kTestAppSupplementalUid = multiuser_get_supplemental_uid(kTestUserId, kTestAppId);
+const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId);
+const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId);
 
 #define FLAG_FORCE InstalldNativeService::FLAG_FORCE
 
@@ -103,18 +110,18 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
-static std::string get_full_path(const char* path) {
-    return StringPrintf("%s/%s", kTestPath, path);
+static std::string get_full_path(const std::string& path) {
+    return StringPrintf("%s/%s", kTestPath, path.c_str());
 }
 
-static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
     const std::string fullPath = get_full_path(path);
     EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
     EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
     EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
 }
 
-static int create(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
     int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
     EXPECT_NE(fd, -1);
     EXPECT_EQ(::fchown(fd, owner, group), 0);
@@ -122,8 +129,8 @@
     return fd;
 }
 
-static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
-    EXPECT_EQ(::close(create(path, owner, group, mode)), 0);
+static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+    EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0);
 }
 
 static int stat_gid(const char* path) {
@@ -138,7 +145,7 @@
     return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
 }
 
-static bool exists(const char* path) {
+static bool exists(const std::string& path) {
     return ::access(get_full_path(path).c_str(), F_OK) == 0;
 }
 
@@ -161,10 +168,11 @@
     return result;
 }
 
-static bool exists_renamed_deleted_dir() {
-    return find_file(kTestPath, [](const std::string& name, bool is_dir) {
-        return is_dir && is_renamed_deleted_dir(name);
-    });
+static bool exists_renamed_deleted_dir(const std::string& rootDirectory) {
+    return find_file((std::string(kTestPath) + rootDirectory).c_str(),
+                     [](const std::string& name, bool is_dir) {
+                         return is_dir && is_renamed_deleted_dir(name);
+                     });
 }
 
 class ServiceTest : public testing::Test {
@@ -179,197 +187,205 @@
         service = new InstalldNativeService();
         testUuid = kTestUuid;
         system("rm -rf /data/local/tmp/user");
+        system("rm -rf /data/local/tmp/misc_ce");
+        system("rm -rf /data/local/tmp/misc_de");
         system("mkdir -p /data/local/tmp/user/0");
-
+        system("mkdir -p /data/local/tmp/misc_ce/0/sdksandbox");
+        system("mkdir -p /data/local/tmp/misc_de/0/sdksandbox");
         init_globals_from_data_and_root();
     }
 
     virtual void TearDown() {
         delete service;
         system("rm -rf /data/local/tmp/user");
+        system("rm -rf /data/local/tmp/misc_ce");
+        system("rm -rf /data/local/tmp/misc_de");
     }
 };
 
 TEST_F(ServiceTest, FixupAppData_Upgrade) {
     LOG(INFO) << "FixupAppData_Upgrade";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/normal", 10000, 10000, 0700);
-    mkdir("com.example/cache", 10000, 10000, 0700);
-    touch("com.example/cache/file", 10000, 10000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/normal", 10000, 10000, 0700);
+    mkdir("user/0/com.example/cache", 10000, 10000, 0700);
+    touch("user/0/com.example/cache/file", 10000, 10000, 0700);
 
     service->fixupAppData(testUuid, 0);
 
-    EXPECT_EQ(10000, stat_gid("com.example/normal"));
-    EXPECT_EQ(20000, stat_gid("com.example/cache"));
-    EXPECT_EQ(20000, stat_gid("com.example/cache/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/normal"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/cache"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/cache/file"));
 
-    EXPECT_EQ(0700, stat_mode("com.example/normal"));
-    EXPECT_EQ(02771, stat_mode("com.example/cache"));
-    EXPECT_EQ(0700, stat_mode("com.example/cache/file"));
+    EXPECT_EQ(0700, stat_mode("user/0/com.example/normal"));
+    EXPECT_EQ(02771, stat_mode("user/0/com.example/cache"));
+    EXPECT_EQ(0700, stat_mode("user/0/com.example/cache/file"));
 }
 
 TEST_F(ServiceTest, FixupAppData_Moved) {
     LOG(INFO) << "FixupAppData_Moved";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
     service->fixupAppData(testUuid, 0);
 
-    EXPECT_EQ(10000, stat_gid("com.example/foo"));
-    EXPECT_EQ(20000, stat_gid("com.example/foo/file"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
 
     service->fixupAppData(testUuid, FLAG_FORCE);
 
-    EXPECT_EQ(10000, stat_gid("com.example/foo"));
-    EXPECT_EQ(10000, stat_gid("com.example/foo/file"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
 }
 
 TEST_F(ServiceTest, DestroyUserData) {
     LOG(INFO) << "DestroyUserData";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+    EXPECT_TRUE(exists("user/0/com.example/foo"));
+    EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+    EXPECT_TRUE(exists("user/0/com.example/bar"));
+    EXPECT_TRUE(exists("user/0/com.example/bar/file"));
 
     service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE);
 
-    EXPECT_FALSE(exists("com.example/foo"));
-    EXPECT_FALSE(exists("com.example/foo/file"));
-    EXPECT_FALSE(exists("com.example/bar"));
-    EXPECT_FALSE(exists("com.example/bar/file"));
+    EXPECT_FALSE(exists("user/0/com.example/foo"));
+    EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+    EXPECT_FALSE(exists("user/0/com.example/bar"));
+    EXPECT_FALSE(exists("user/0/com.example/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+    EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
 }
 
 TEST_F(ServiceTest, DestroyAppData) {
     LOG(INFO) << "DestroyAppData";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+    EXPECT_TRUE(exists("user/0/com.example/foo"));
+    EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+    EXPECT_TRUE(exists("user/0/com.example/bar"));
+    EXPECT_TRUE(exists("user/0/com.example/bar/file"));
 
     service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0);
 
-    EXPECT_FALSE(exists("com.example/foo"));
-    EXPECT_FALSE(exists("com.example/foo/file"));
-    EXPECT_FALSE(exists("com.example/bar"));
-    EXPECT_FALSE(exists("com.example/bar/file"));
+    EXPECT_FALSE(exists("user/0/com.example/foo"));
+    EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+    EXPECT_FALSE(exists("user/0/com.example/bar"));
+    EXPECT_FALSE(exists("user/0/com.example/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+    EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
 }
 
 TEST_F(ServiceTest, CleanupInvalidPackageDirs) {
     LOG(INFO) << "CleanupInvalidPackageDirs";
 
-    mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700);
-    mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
-    touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
-    mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
-    touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
+    std::string rootDirectoryPrefix[] = {"user/0", "misc_ce/0/sdksandbox", "misc_de/0/sdksandbox"};
+    for (auto& prefix : rootDirectoryPrefix) {
+        mkdir(prefix + "/5b14b6458a44==deleted==", 10000, 10000, 0700);
+        mkdir(prefix + "/5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
+        touch(prefix + "/5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
+        touch(prefix + "/5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
 
-    auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
+        auto fd = create(prefix + "/5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
 
-    mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700);
-    mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
-    touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
-    mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
-    touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted", 10000, 10000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
+        touch(prefix + "/b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
+        touch(prefix + "/b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/com.example", 10000, 10000, 0700);
+        mkdir(prefix + "/com.example/foo", 10000, 10000, 0700);
+        touch(prefix + "/com.example/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/com.example/bar", 10000, 20000, 0700);
+        touch(prefix + "/com.example/bar/file", 10000, 20000, 0700);
 
-    mkdir("==deleted==", 10000, 10000, 0700);
-    mkdir("==deleted==/foo", 10000, 10000, 0700);
-    touch("==deleted==/foo/file", 10000, 20000, 0700);
-    mkdir("==deleted==/bar", 10000, 20000, 0700);
-    touch("==deleted==/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/==deleted==", 10000, 10000, 0700);
+        mkdir(prefix + "/==deleted==/foo", 10000, 10000, 0700);
+        touch(prefix + "/==deleted==/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/==deleted==/bar", 10000, 20000, 0700);
+        touch(prefix + "/==deleted==/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
 
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
 
-    EXPECT_TRUE(exists("==deleted==/foo"));
-    EXPECT_TRUE(exists("==deleted==/foo/file"));
-    EXPECT_TRUE(exists("==deleted==/bar"));
-    EXPECT_TRUE(exists("==deleted==/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/foo"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/bar"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/bar/file"));
 
-    EXPECT_TRUE(exists_renamed_deleted_dir());
+        EXPECT_TRUE(exists_renamed_deleted_dir("/" + prefix));
 
-    service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
+        service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
 
-    EXPECT_EQ(::close(fd), 0);
+        EXPECT_EQ(::close(fd), 0);
 
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
 
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
 
-    EXPECT_FALSE(exists("==deleted==/foo"));
-    EXPECT_FALSE(exists("==deleted==/foo/file"));
-    EXPECT_FALSE(exists("==deleted==/bar"));
-    EXPECT_FALSE(exists("==deleted==/bar/file"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/foo"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/foo/file"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/bar"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+        EXPECT_FALSE(exists_renamed_deleted_dir(prefix));
+    }
 }
 
 TEST_F(ServiceTest, HashSecondaryDex) {
     LOG(INFO) << "HashSecondaryDex";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -389,7 +405,7 @@
     LOG(INFO) << "HashSecondaryDex_NoSuch";
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -399,12 +415,12 @@
 TEST_F(ServiceTest, HashSecondaryDex_Unreadable) {
     LOG(INFO) << "HashSecondaryDex_Unreadable";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0300);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0300);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -414,12 +430,12 @@
 TEST_F(ServiceTest, HashSecondaryDex_WrongApp) {
     LOG(INFO) << "HashSecondaryDex_WrongApp";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_FAIL(service->hashSecondaryDexFile(
         dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result));
 }
@@ -491,6 +507,19 @@
         system(removeCommand.c_str());
     }
 }
+TEST_F(ServiceTest, GetAppSizeWrongSizes) {
+    int32_t externalStorageAppId = -1;
+    std::vector<int64_t> externalStorageSize;
+
+    std::vector<std::string> codePaths;
+    std::vector<std::string> packageNames = {"package1", "package2"};
+    std::vector<int64_t> ceDataInodes = {0};
+
+    EXPECT_BINDER_FAIL(service->getAppSize(std::nullopt, packageNames, 0,
+                                           InstalldNativeService::FLAG_USE_QUOTA,
+                                           externalStorageAppId, ceDataInodes, codePaths,
+                                           &externalStorageSize));
+}
 static bool mkdirs(const std::string& path, mode_t mode) {
     struct stat sb;
     if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
@@ -936,28 +965,48 @@
           "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
 }
 
-class AppSupplementalDataTest : public testing::Test {
+class SdkSandboxDataTest : public testing::Test {
 public:
-    void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) {
+    void CheckFileAccess(const std::string& path, uid_t uid, gid_t gid, mode_t mode) {
         const auto fullPath = "/data/local/tmp/" + path;
         ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath;
         struct stat st;
         ASSERT_EQ(0, stat(fullPath.c_str(), &st));
         ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath;
-        ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath;
+        ASSERT_EQ(gid, st.st_gid) << "For path: " << fullPath;
         ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath;
     }
 
     bool exists(const char* path) { return ::access(path, F_OK) == 0; }
 
     // Creates a default CreateAppDataArgs object
-    android::os::CreateAppDataArgs createAppDataArgs() {
+    android::os::CreateAppDataArgs createAppDataArgs(const std::string& packageName) {
         android::os::CreateAppDataArgs args;
         args.uuid = kTestUuid;
-        args.packageName = "com.foo";
+        args.packageName = packageName;
         args.userId = kTestUserId;
         args.appId = kTestAppId;
         args.seInfo = "default";
+        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
+        return args;
+    }
+
+    android::os::ReconcileSdkDataArgs reconcileSdkDataArgs(
+            std::string packageName, std::vector<std::string> codeNames,
+            std::vector<std::string> randomSuffixes) {
+        android::os::ReconcileSdkDataArgs args;
+        args.uuid = kTestUuid;
+        args.packageName = packageName;
+        for (const auto& codeName : codeNames) {
+            args.sdkPackageNames.push_back(codeName);
+        }
+        for (const auto& randomSuffix : randomSuffixes) {
+            args.randomSuffixes.push_back(randomSuffix);
+        }
+        args.userId = kTestUserId;
+        args.appId = kTestAppId;
+        args.previousAppId = -1;
+        args.seInfo = "default";
         args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
         return args;
     }
@@ -973,8 +1022,8 @@
         clearAppData();
         ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700));
         ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700));
-        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/supplemental", 0700));
-        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/supplemental", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/sdksandbox", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/sdksandbox", 0700));
 
         init_globals_from_data_and_root();
     }
@@ -986,69 +1035,391 @@
 
 private:
     void clearAppData() {
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true));
-        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
     }
 };
 
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo";
+    CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooCePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(fooCePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string fooDePath = "misc_de/0/sdksandbox/com.foo";
+    CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooDePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(fooDePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
 
     // Create the app user data.
     ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
 
-    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
-                    S_IFDIR | 0700);
-    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
-                    S_IFDIR | S_ISGID | 0771);
-
-    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess("misc_de/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
-                    S_IFDIR | 0700);
-    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
-                    S_IFDIR | S_ISGID | 0771);
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlagDeletesExisting) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
-    args.flags = FLAG_STORAGE_CE;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK;
 
     // Create the app user data.
     ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
 
     // Only CE paths should exist
-    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751);
 
     // DE paths should not exist
-    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/supplemental/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutCeFlag) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
-    args.flags = FLAG_STORAGE_DE;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
 
     // Create the app user data.
     ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
 
     // CE paths should not exist
-    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/supplemental/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
 
     // Only DE paths should exist
-    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751);
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdk data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    const std::string barCePath = "misc_ce/0/sdksandbox/com.foo/bar@random1";
+    CheckFileAccess(barCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1";
+    CheckFileAccess(barDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_PackageNameCannotUseRandomSuffixSeparator) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar@illegal"}, {"random1"});
+
+    // Create the sdksandbox data.
+    auto status = service->reconcileSdkData(args);
+    ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+    ASSERT_EQ(status.exceptionMessage(), "Package name bar@illegal is malformed");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_NotEnoughRandomSuffix) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1"});
+
+    // Create the sdksandbox data.
+    auto status = service->reconcileSdkData(args);
+    ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+    ASSERT_EQ(status.exceptionMessage(), "Not enough random suffix. Required 2, received 1.");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_DirectoryNotCreatedIfAlreadyExistsIgnoringSuffix) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdksandbox data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Retry with different random suffix
+    args.randomSuffixes[0] = "r10";
+    args.randomSuffixes[1] = "r20";
+
+    // Create the sdksandbox data again
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Previous directories from first attempt should exist
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar@random1", kTestSdkSandboxUid, kNobodyUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+                    S_IFDIR | 0700);
+    // No new directories should be created on second attempt
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@r10"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo/bar@r20"));
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_ExtraCodeDirectoriesAreDeleted) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdksandbox data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Retry with different package name
+    args.sdkPackageNames[0] = "bar.diff";
+
+    // Create the sdksandbox data again
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // New directoris should exist
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar.diff@random1", kTestSdkSandboxUid, kNobodyUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+                    S_IFDIR | 0700);
+    // Directory for old unreferred sdksandbox package name should be removed
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@random1"));
+}
+
+class DestroyAppDataTest : public SdkSandboxDataTest {};
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithCeAndDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  args.flags, result.ceDataInode));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  FLAG_STORAGE_CE, result.ceDataInode));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+}
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutCeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  FLAG_STORAGE_DE, result.ceDataInode));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+class ClearAppDataTest : public SdkSandboxDataTest {
+public:
+    void createTestSdkData(const std::string& packageName, std::vector<std::string> sdkNames) {
+        const auto& cePackagePath = "/data/local/tmp/misc_ce/0/sdksandbox/" + packageName;
+        const auto& dePackagePath = "/data/local/tmp/misc_de/0/sdksandbox/" + packageName;
+        ASSERT_TRUE(mkdirs(cePackagePath + "/shared/cache", 0700));
+        ASSERT_TRUE(mkdirs(cePackagePath + "shared/code_cache", 0700));
+        ASSERT_TRUE(mkdirs(dePackagePath + "/shared/cache", 0700));
+        ASSERT_TRUE(mkdirs(dePackagePath + "/shared/code_cache", 0700));
+        std::ofstream{cePackagePath + "/shared/cache/cachedTestData.txt"};
+        for (auto sdkName : sdkNames) {
+            ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/cache", 0700));
+            ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/cache", 0700));
+            ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/code_cache", 0700));
+            ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/code_cache", 0700));
+            std::ofstream{cePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+            std::ofstream{cePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+            std::ofstream{dePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+            std::ofstream{dePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+        }
+    }
+};
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_CE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_CE |
+                                          (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(
+            fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_DE |
+                                          (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(
+            fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+                                                FLAG_STORAGE_CE, result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+                                                FLAG_STORAGE_DE, result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2")));
+}
+
+class DestroyUserDataTest : public SdkSandboxDataTest {};
+
+TEST_F(DestroyUserDataTest, DestroySdkData_WithCeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    // Destroy user data
+    ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_CE));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox"));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox"));
+}
+
+TEST_F(DestroyUserDataTest, DestroySdkData_WithDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "com.foo";
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    // Destroy user data
+    ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_DE));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox"));
 }
 
 }  // namespace installd
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index a7a8624..38c1c05 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <android-base/logging.h>
 #include <android-base/scopeguard.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "InstalldNativeService.h"
@@ -45,6 +48,8 @@
 namespace android {
 namespace installd {
 
+using ::testing::UnorderedElementsAre;
+
 class UtilsTest : public testing::Test {
 protected:
     virtual void SetUp() {
@@ -656,38 +661,98 @@
     ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
 }
 
-TEST_F(UtilsTest, TestSupplementalDataPaths) {
+TEST_F(UtilsTest, TestForEachSubdir) {
+    auto deleter = [&]() {
+        delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */);
+    };
+    auto scope_guard = android::base::make_scope_guard(deleter);
+
+    system("mkdir -p /data/local/tmp/user/0/com.foo");
+    system("mkdir -p /data/local/tmp/user/0/com.bar");
+    system("touch /data/local/tmp/user/0/some-file");
+
+    std::vector<std::string> result;
+    foreach_subdir("/data/local/tmp/user/0",
+                   [&](const std::string &filename) { result.push_back(filename); });
+
+    EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar"));
+}
+
+TEST_F(UtilsTest, TestSdkSandboxDataPaths) {
     // Ce data paths
-    EXPECT_EQ("/data/misc_ce/0/supplemental",
-              create_data_misc_supplemental_path(nullptr, /*isCeData=*/true, 0));
-    EXPECT_EQ("/data/misc_ce/10/supplemental",
-              create_data_misc_supplemental_path(nullptr, true, 10));
+    EXPECT_EQ("/data/misc_ce/0/sdksandbox",
+              create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/true, 0));
+    EXPECT_EQ("/data/misc_ce/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, true, 10));
 
-    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo",
-              create_data_misc_supplemental_package_path(nullptr, true, 0, "com.foo"));
-    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo",
-              create_data_misc_supplemental_package_path(nullptr, true, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo",
+              create_data_misc_sdk_sandbox_package_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo",
+              create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo"));
 
-    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo/shared",
-              create_data_misc_supplemental_shared_path(nullptr, true, 0, "com.foo"));
-    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo/shared",
-              create_data_misc_supplemental_shared_path(nullptr, true, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared",
+              create_data_misc_sdk_sandbox_shared_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared",
+              create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/bar@random",
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar", "random"));
 
     // De data paths
-    EXPECT_EQ("/data/misc_de/0/supplemental",
-              create_data_misc_supplemental_path(nullptr, /*isCeData=*/false, 0));
-    EXPECT_EQ("/data/misc_de/10/supplemental",
-              create_data_misc_supplemental_path(nullptr, false, 10));
+    EXPECT_EQ("/data/misc_de/0/sdksandbox",
+              create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/false, 0));
+    EXPECT_EQ("/data/misc_de/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, false, 10));
 
-    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo",
-              create_data_misc_supplemental_package_path(nullptr, false, 0, "com.foo"));
-    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo",
-              create_data_misc_supplemental_package_path(nullptr, false, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo",
+              create_data_misc_sdk_sandbox_package_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo",
+              create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo"));
 
-    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo/shared",
-              create_data_misc_supplemental_shared_path(nullptr, false, 0, "com.foo"));
-    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo/shared",
-              create_data_misc_supplemental_shared_path(nullptr, false, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared",
+              create_data_misc_sdk_sandbox_shared_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared",
+              create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/bar@random",
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "bar",
+                                                    "random"));
+}
+
+TEST_F(UtilsTest, WaitChild) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child */
+        // Do nothing.
+        _exit(0);
+    }
+    /* parent */
+    int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/100);
+    EXPECT_TRUE(WIFEXITED(return_code));
+    EXPECT_EQ(WEXITSTATUS(return_code), 0);
+}
+
+TEST_F(UtilsTest, WaitChildTimeout) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child */
+        sleep(1);
+        _exit(0);
+    }
+    /* parent */
+    int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/1);
+    EXPECT_FALSE(WIFEXITED(return_code));
+    EXPECT_EQ(WTERMSIG(return_code), SIGKILL);
+}
+
+TEST_F(UtilsTest, RemoveFileAtFd) {
+    std::string filename = "/data/local/tmp/tempfile-XXXXXX";
+    int fd = mkstemp(filename.data());
+    ASSERT_GE(fd, 0);
+    ASSERT_EQ(access(filename.c_str(), F_OK), 0);
+
+    std::string actual_filename;
+    remove_file_at_fd(fd, &actual_filename);
+    EXPECT_NE(access(filename.c_str(), F_OK), 0);
+    EXPECT_EQ(filename, actual_filename);
+
+    close(fd);
 }
 
 }  // namespace installd
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 3ce4b67..8cfd123 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -19,18 +19,21 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <fts.h>
+#include <poll.h>
 #include <stdlib.h>
 #include <sys/capability.h>
+#include <sys/pidfd.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
+#include <unistd.h>
 #include <uuid/uuid.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
@@ -195,42 +198,55 @@
 }
 
 /**
- * Create the path name where supplemental data for all apps will be stored.
- * E.g. /data/misc_ce/0/supplemental
+ * Create the path name where sdk_sandbox data for all apps will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox
  */
-std::string create_data_misc_supplemental_path(const char* uuid, bool isCeData, userid_t user) {
+std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, userid_t user) {
     std::string data(create_data_path(uuid));
     if (isCeData) {
-        return StringPrintf("%s/misc_ce/%d/supplemental", data.c_str(), user);
+        return StringPrintf("%s/misc_ce/%d/sdksandbox", data.c_str(), user);
     } else {
-        return StringPrintf("%s/misc_de/%d/supplemental", data.c_str(), user);
+        return StringPrintf("%s/misc_de/%d/sdksandbox", data.c_str(), user);
     }
 }
 
 /**
  * Create the path name where code data for all codes in a particular app will be stored.
- * E.g. /data/misc_ce/0/supplemental/<app-name>
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>
  */
-std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
-                                                       userid_t user, const char* package_name) {
+std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
+                                                      userid_t user, const char* package_name) {
     check_package_name(package_name);
     return StringPrintf("%s/%s",
-                        create_data_misc_supplemental_path(volume_uuid, isCeData, user).c_str(),
+                        create_data_misc_sdk_sandbox_path(volume_uuid, isCeData, user).c_str(),
                         package_name);
 }
 
 /**
  * Create the path name where shared code data for a particular app will be stored.
- * E.g. /data/misc_ce/0/supplemental/<app-name>/shared
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/shared
  */
-std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
-                                                      userid_t user, const char* package_name) {
+std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
+                                                     userid_t user, const char* package_name) {
     return StringPrintf("%s/shared",
-                        create_data_misc_supplemental_package_path(volume_uuid, isCeData, user,
-                                                                   package_name)
+                        create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user,
+                                                                  package_name)
                                 .c_str());
 }
 
+/**
+ * Create the path name where per-code level data for a particular app will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/<sdk-name>-<random-suffix>
+ */
+std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+                                                  userid_t user, const char* package_name,
+                                                  const char* sdk_name, const char* randomSuffix) {
+    check_package_name(sdk_name);
+    auto package_path =
+            create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, package_name);
+    return StringPrintf("%s/%s@%s", package_path.c_str(), sdk_name, randomSuffix);
+}
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
     return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
 }
@@ -693,6 +709,34 @@
     return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
 }
 
+// Collects filename of subdirectories of given directory and passes it to the function
+int foreach_subdir(const std::string& pathname, const std::function<void(const std::string&)> fn) {
+    auto dir = open_dir(pathname.c_str());
+    if (!dir) return -1;
+
+    int dfd = dirfd(dir.get());
+    if (dfd < 0) {
+        ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno));
+        return -1;
+    }
+
+    struct dirent* de;
+    while ((de = readdir(dir.get()))) {
+        if (de->d_type != DT_DIR) {
+            continue;
+        }
+
+        std::string name{de->d_name};
+        // always skip "." and ".."
+        if (name == "." || name == "..") {
+            continue;
+        }
+        fn(name);
+    }
+
+    return 0;
+}
+
 void cleanup_invalid_package_dirs_under_path(const std::string& pathname) {
     auto dir = open_dir(pathname.c_str());
     if (!dir) {
@@ -1096,30 +1140,45 @@
     return fs_prepare_dir(path.c_str(), 0750, uid, gid);
 }
 
-int wait_child(pid_t pid)
-{
+static int wait_child(pid_t pid) {
     int status;
-    pid_t got_pid;
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, /*options=*/0));
 
-    while (1) {
-        got_pid = waitpid(pid, &status, 0);
-        if (got_pid == -1 && errno == EINTR) {
-            printf("waitpid interrupted, retrying\n");
-        } else {
-            break;
-        }
-    }
     if (got_pid != pid) {
-        ALOGW("waitpid failed: wanted %d, got %d: %s\n",
-            (int) pid, (int) got_pid, strerror(errno));
-        return 1;
+        PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+        return W_EXITCODE(/*exit_code=*/255, /*signal_number=*/0);
     }
 
-    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-        return 0;
-    } else {
-        return status;      /* always nonzero */
+    return status;
+}
+
+int wait_child_with_timeout(pid_t pid, int timeout_ms) {
+    int pidfd = pidfd_open(pid, /*flags=*/0);
+    if (pidfd < 0) {
+        PLOG(ERROR) << "pidfd_open failed for pid " << pid;
+        kill(pid, SIGKILL);
+        return wait_child(pid);
     }
+
+    struct pollfd pfd;
+    pfd.fd = pidfd;
+    pfd.events = POLLIN;
+    int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms));
+
+    close(pidfd);
+
+    if (poll_ret < 0) {
+        PLOG(ERROR) << "poll failed for pid " << pid;
+        kill(pid, SIGKILL);
+        return wait_child(pid);
+    }
+    if (poll_ret == 0) {
+        LOG(WARNING) << "Child process " << pid << " timed out after " << timeout_ms
+                     << "ms. Killing it";
+        kill(pid, SIGKILL);
+        return wait_child(pid);
+    }
+    return wait_child(pid);
 }
 
 /**
@@ -1332,5 +1391,27 @@
     }
 }
 
+bool remove_file_at_fd(int fd, /*out*/ std::string* path) {
+    char path_buffer[PATH_MAX + 1];
+    std::string proc_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+    ssize_t len = readlink(proc_path.c_str(), path_buffer, PATH_MAX);
+    if (len < 0) {
+        PLOG(WARNING) << "Could not remove file at fd " << fd << ": Failed to get file path";
+        return false;
+    }
+    path_buffer[len] = '\0';
+    if (path != nullptr) {
+        *path = path_buffer;
+    }
+    if (unlink(path_buffer) != 0) {
+        if (errno == ENOENT) {
+            return true;
+        }
+        PLOG(WARNING) << "Could not remove file at path " << path_buffer;
+        return false;
+    }
+    return true;
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 2db1623..54d77f9 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -32,6 +32,7 @@
 
 #define MEASURE_DEBUG 0
 #define FIXUP_DEBUG 0
+#define SDK_DEBUG 1
 
 #define BYPASS_QUOTA 0
 #define BYPASS_SDCARDFS 0
@@ -60,12 +61,15 @@
 std::string create_data_user_ce_package_path_as_user_link(
         const char* volume_uuid, userid_t userid, const char* package_name);
 
-std::string create_data_misc_supplemental_path(const char* volume_uuid, bool isCeData,
-                                               userid_t userid);
-std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
-                                                       userid_t userid, const char* package_name);
-std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCeData,
+                                              userid_t userid);
+std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
                                                       userid_t userid, const char* package_name);
+std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
+                                                     userid_t userid, const char* package_name);
+std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+                                                  userid_t userid, const char* package_name,
+                                                  const char* sdk_name, const char* randomSuffix);
 
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
@@ -130,6 +134,8 @@
 bool is_renamed_deleted_dir(const std::string& path);
 int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true);
 
+int foreach_subdir(const std::string& pathname, std::function<void(const std::string&)> fn);
+
 void cleanup_invalid_package_dirs_under_path(const std::string& pathname);
 
 int delete_dir_contents(const char *pathname,
@@ -160,7 +166,8 @@
 
 int ensure_config_user_dirs(userid_t userid);
 
-int wait_child(pid_t pid);
+// Waits for a child process, or kills it if it times out. Returns the exit code.
+int wait_child_with_timeout(pid_t pid, int timeout_ms);
 
 int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
         uid_t uid, gid_t gid);
@@ -177,6 +184,10 @@
 
 void drop_capabilities(uid_t uid);
 
+// Removes a file specified by a file descriptor. Returns true on success. Reports the file path to
+// `path` if present.
+bool remove_file_at_fd(int fd, /*out*/ std::string* path = nullptr);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
index 60d6492..8c000a1 100644
--- a/cmds/installd/view_compiler.cpp
+++ b/cmds/installd/view_compiler.cpp
@@ -33,7 +33,13 @@
 namespace android {
 namespace installd {
 
-using base::unique_fd;
+namespace {
+
+using ::android::base::unique_fd;
+
+constexpr int kTimeoutMs = 300000;
+
+} // namespace
 
 bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
                    int uid) {
@@ -88,7 +94,17 @@
         _exit(1);
     }
 
-    return wait_child(pid) == 0;
+    int return_code = wait_child_with_timeout(pid, kTimeoutMs);
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "viewcompiler failed for " << package_name << ":" << apk_path;
+        if (WTERMSIG(return_code) == SIGKILL) {
+            // If the subprocess is killed while it's writing to the file, the file is likely
+            // corrupted, so we should remove it.
+            remove_file_at_fd(outfd.get());
+        }
+        return false;
+    }
+    return WEXITSTATUS(return_code) == 0;
 }
 
 } // namespace installd
diff --git a/include/input/Input.h b/include/input/Input.h
index 2837add..b23a951 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -814,6 +814,8 @@
     static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
     static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
                                                const PointerCoords&);
+    static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
+                                                    const PointerCoords&);
 
 protected:
     int32_t mAction;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7448308..63d87da 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -166,6 +166,7 @@
         "-Wextra-semi",
         "-Werror",
         "-Wzero-as-null-pointer-constant",
+        "-Wreorder-init-list",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 0970ca5..01b25d3 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <set>
 
+#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
@@ -281,9 +282,11 @@
             err = pingBinder();
             break;
         case EXTENSION_TRANSACTION:
+            CHECK(reply != nullptr);
             err = reply->writeStrongBinder(getExtension());
             break;
         case DEBUG_PID_TRANSACTION:
+            CHECK(reply != nullptr);
             err = reply->writeInt32(getDebugPid());
             break;
         case SET_RPC_CLIENT_TRANSACTION: {
@@ -590,6 +593,7 @@
 {
     switch (code) {
         case INTERFACE_TRANSACTION:
+            CHECK(reply != nullptr);
             reply->writeString16(getInterfaceDescriptor());
             return NO_ERROR;
 
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 056ef0a..921e57c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -72,29 +72,29 @@
     e.cleanupCookie = cleanupCookie;
     e.func = func;
 
-    if (ssize_t idx = mObjects.indexOfKey(objectID); idx >= 0) {
+    if (mObjects.find(objectID) != mObjects.end()) {
         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;
+        return mObjects[objectID].object;
     }
 
-    mObjects.add(objectID, e);
+    mObjects.insert({objectID, e});
     return nullptr;
 }
 
 void* BpBinder::ObjectManager::find(const void* objectID) const
 {
-    const ssize_t i = mObjects.indexOfKey(objectID);
-    if (i < 0) return nullptr;
-    return mObjects.valueAt(i).object;
+    auto i = mObjects.find(objectID);
+    if (i == mObjects.end()) return nullptr;
+    return i->second.object;
 }
 
 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);
+    auto i = mObjects.find(objectID);
+    if (i == mObjects.end()) return nullptr;
+    void* value = i->second.object;
+    mObjects.erase(i);
     return value;
 }
 
@@ -102,10 +102,10 @@
 {
     const size_t N = mObjects.size();
     ALOGV("Killing %zu objects in manager %p", N, this);
-    for (size_t i=0; i<N; i++) {
-        const entry_t& e = mObjects.valueAt(i);
+    for (auto i : mObjects) {
+        const entry_t& e = i.second;
         if (e.func != nullptr) {
-            e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
+            e.func(i.first, e.object, e.cleanupCookie);
         }
     }
 
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index bd974b0..9c7ff97 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -31,9 +31,10 @@
 #include <binder/Parcel.h>
 #include <log/log.h>
 
-#include <utils/KeyedVector.h>
 #include <utils/threads.h>
 
+#include <map>
+
 #define VERBOSE   0
 
 namespace android {
@@ -63,7 +64,7 @@
     void free_heap(const wp<IBinder>& binder);
 
     Mutex mHeapCacheLock;  // Protects entire vector below.
-    KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+    std::map<wp<IBinder>, heap_info_t> mHeapCache;
     // We do not use the copy-on-write capabilities of KeyedVector.
     // TODO: Reimplemement based on standard C++ container?
 };
@@ -434,9 +435,9 @@
 sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
 {
     Mutex::Autolock _l(mHeapCacheLock);
-    ssize_t i = mHeapCache.indexOfKey(binder);
-    if (i>=0) {
-        heap_info_t& info = mHeapCache.editValueAt(i);
+    auto i = mHeapCache.find(binder);
+    if (i != mHeapCache.end()) {
+        heap_info_t& info = i->second;
         ALOGD_IF(VERBOSE,
                 "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                 binder.get(), info.heap.get(),
@@ -452,7 +453,7 @@
         info.count = 1;
         //ALOGD("adding binder=%p, heap=%p, count=%d",
         //      binder.get(), info.heap.get(), info.count);
-        mHeapCache.add(binder, info);
+        mHeapCache.insert({binder, info});
         return info.heap;
     }
 }
@@ -466,9 +467,9 @@
     sp<IMemoryHeap> rel;
     {
         Mutex::Autolock _l(mHeapCacheLock);
-        ssize_t i = mHeapCache.indexOfKey(binder);
-        if (i>=0) {
-            heap_info_t& info(mHeapCache.editValueAt(i));
+        auto i = mHeapCache.find(binder);
+        if (i != mHeapCache.end()) {
+            heap_info_t& info = i->second;
             if (--info.count == 0) {
                 ALOGD_IF(VERBOSE,
                         "removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
@@ -477,8 +478,8 @@
                         static_cast<BpMemoryHeap*>(info.heap.get())
                             ->mHeapId.load(memory_order_relaxed),
                         info.count);
-                rel = mHeapCache.valueAt(i).heap;
-                mHeapCache.removeItemsAt(i);
+                rel = i->second.heap;
+                mHeapCache.erase(i);
             }
         } else {
             ALOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
@@ -490,23 +491,23 @@
 {
     sp<IMemoryHeap> realHeap;
     Mutex::Autolock _l(mHeapCacheLock);
-    ssize_t i = mHeapCache.indexOfKey(binder);
-    if (i>=0)   realHeap = mHeapCache.valueAt(i).heap;
-    else        realHeap = interface_cast<IMemoryHeap>(binder);
+    auto i = mHeapCache.find(binder);
+    if (i != mHeapCache.end())
+        realHeap = i->second.heap;
+    else
+        realHeap = interface_cast<IMemoryHeap>(binder);
     return realHeap;
 }
 
 void HeapCache::dump_heaps()
 {
     Mutex::Autolock _l(mHeapCacheLock);
-    int c = mHeapCache.size();
-    for (int i=0 ; i<c ; i++) {
-        const heap_info_t& info = mHeapCache.valueAt(i);
+    for (const auto& i : mHeapCache) {
+        const heap_info_t& info = i.second;
         BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
-        ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
-                mHeapCache.keyAt(i).unsafe_get(),
-                info.heap.get(), info.count,
-                h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize);
+        ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)", i.first.unsafe_get(),
+              info.heap.get(), info.count, h->mHeapId.load(memory_order_relaxed), h->mBase,
+              h->mSize);
     }
 }
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 13f0a4c..f79075d 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1199,7 +1199,8 @@
 
     case BR_DECREFS:
         refs = (RefBase::weakref_type*)mIn.readPointer();
-        obj = (BBinder*)mIn.readPointer();
+        // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
+        obj = (BBinder*)mIn.readPointer(); // consume
         // NOTE: This assertion is not valid, because the object may no
         // longer exist (thus the (BBinder*)cast above resulting in a different
         // memory address).
@@ -1409,7 +1410,7 @@
                                               uint32_t *async_received)
 {
     int ret = 0;
-    binder_frozen_status_info info;
+    binder_frozen_status_info info = {};
     info.pid = pid;
 
 #if defined(__ANDROID__)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f84f639..504c6c2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -63,7 +63,7 @@
 // This macro should never be used at runtime, as a too large value
 // of s could cause an integer overflow. Instead, you should always
 // use the wrapper function pad_size()
-#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
+#define PAD_SIZE_UNSAFE(s) (((s) + 3) & ~3UL)
 
 static size_t pad_size(size_t s) {
     if (s > (std::numeric_limits<size_t>::max() - 3)) {
@@ -200,7 +200,6 @@
     }
 
     flat_binder_object obj;
-    obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
 
     int schedBits = 0;
     if (!IPCThreadState::self()->backgroundSchedulingDisabled()) {
@@ -221,6 +220,7 @@
             const int32_t handle = proxy ? proxy->getPrivateAccessor().binderHandle() : 0;
             obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
+            obj.flags = 0;
             obj.handle = handle;
             obj.cookie = 0;
         } else {
@@ -231,6 +231,7 @@
                 // override value, since it is set explicitly
                 schedBits = schedPolicyMask(policy, priority);
             }
+            obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
             if (local->isRequestingSid()) {
                 obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
             }
@@ -243,6 +244,7 @@
         }
     } else {
         obj.hdr.type = BINDER_TYPE_BINDER;
+        obj.flags = 0;
         obj.binder = 0;
         obj.cookie = 0;
     }
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1821729..b14a838 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -19,6 +19,7 @@
 #include <binder/ProcessState.h>
 
 #include <android-base/result.h>
+#include <android-base/strings.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -367,8 +368,13 @@
 String8 ProcessState::makeBinderThreadName() {
     int32_t s = android_atomic_add(1, &mThreadPoolSeq);
     pid_t pid = getpid();
+
+    std::string_view driverName = mDriverName.c_str();
+    android::base::ConsumePrefix(&driverName, "/dev/");
+
     String8 name;
-    name.appendFormat("%d_%X:%s", pid, s, mDriverName.c_str());
+    name.appendFormat("%.*s:%d_%X", static_cast<int>(driverName.length()), driverName.data(), pid,
+                      s);
     return name;
 }
 
@@ -403,6 +409,28 @@
     return 0;
 }
 
+#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
+bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
+    static const char* const names[] = {
+        [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
+            DRIVER_FEATURES_PATH "oneway_spam_detection",
+    };
+    int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
+    char on;
+    if (fd == -1) {
+        ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
+                 names[static_cast<int>(feature)], strerror(errno));
+        return false;
+    }
+    if (read(fd, &on, sizeof(on)) == -1) {
+        ALOGE("%s: error reading to %s: %s", __func__,
+                 names[static_cast<int>(feature)], strerror(errno));
+        return false;
+    }
+    close(fd);
+    return on == '1';
+}
+
 status_t ProcessState::enableOnewaySpamDetection(bool enable) {
     uint32_t enableDetection = enable ? 1 : 0;
     if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
@@ -446,7 +474,9 @@
     uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
     result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
     if (result == -1) {
-        ALOGV("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+        ALOGE_IF(ProcessState::isDriverFeatureEnabled(
+                     ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION),
+                 "Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
     }
     return fd;
 }
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index b84395e..e79cb86 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -152,8 +152,13 @@
 }
 
 status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
-    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
-        // std::move'd from fd becomes -1 (!ok())
+    // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable
+    // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`,
+    // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the
+    // lambda.
+    return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId,
+                                               bool incoming) -> status_t {
+        unique_fd fd(raw);
         if (!fd.ok()) {
             fd = request();
             if (!fd.ok()) return BAD_VALUE;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 4ddbce7..2e7084e 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -125,8 +125,8 @@
         auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address),
                                                         BinderNode{
                                                                 .binder = binder,
-                                                                .timesSent = 1,
                                                                 .sentRef = binder,
+                                                                .timesSent = 1,
                                                         }});
         if (inserted) {
             *outAddress = it->first;
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index c0454b6..8deb2fe 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,10 +17,10 @@
 #pragma once
 
 #include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
 
+#include <map>
 #include <unordered_map>
 #include <variant>
 
@@ -110,7 +110,7 @@
             IBinder::object_cleanup_func func;
         };
 
-        KeyedVector<const void*, entry_t> mObjects;
+        std::map<const void*, entry_t> mObjects;
     };
 
     class PrivateAccessor {
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index ea40db8..bb55831 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -56,8 +56,16 @@
     static const int DUMP_FLAG_PROTO = 1 << 4;
 
     /**
-     * Retrieve an existing service, blocking for a few seconds
-     * if it doesn't yet exist.
+     * Retrieve an existing service, blocking for a few seconds if it doesn't yet exist. This
+     * does polling. A more efficient way to make sure you unblock as soon as the service is
+     * available is to use waitForService or to use service notifications.
+     *
+     * Warning: when using this API, typically, you should call it in a loop. It's dangerous to
+     * assume that nullptr could mean that the service is not available. The service could just
+     * be starting. Generally, whether a service exists, this information should be declared
+     * externally (for instance, an Android feature might imply the existence of a service,
+     * a system property, or in the case of services in the VINTF manifest, it can be checked
+     * with isDeclared).
      */
     virtual sp<IBinder>         getService( const String16& name) const = 0;
 
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index cf8d8e4..0deee73 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -91,6 +91,12 @@
      */
     size_t getThreadPoolMaxThreadCount() const;
 
+    enum class DriverFeature {
+        ONEWAY_SPAM_DETECTION,
+    };
+    // Determine whether a feature is supported by the binder driver.
+    static bool isDriverFeatureEnabled(const DriverFeature feature);
+
 private:
     static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 2c471c6..7ea9be7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -268,7 +268,11 @@
     const char* getMessage() const { return AStatus_getMessage(get()); }
 
     std::string getDescription() const {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 30, *)) {
+#else
+        if (__ANDROID_API__ >= 30) {
+#endif
             const char* cStr = AStatus_getDescription(get());
             std::string ret = cStr;
             AStatus_deleteDescription(cStr);
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 09411e7..b3bc7f4 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -256,7 +256,11 @@
     // ourselves. The defaults are harmless.
     AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
 #ifdef HAS_BINDER_SHELL_COMMAND
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
     if (__builtin_available(android 30, *)) {
+#else
+    if (__ANDROID_API__ >= 30) {
+#endif
         AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
     }
 #endif
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 972eca7..28819bb 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -51,7 +51,11 @@
     AParcelableHolder(const AParcelableHolder& other)
         : mParcel(AParcel_create()), mStability(other.mStability) {
         // AParcelableHolder has been introduced in 31.
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
                                AParcel_getDataSize(other.mParcel.get()));
         }
@@ -63,13 +67,21 @@
 
     binder_status_t writeToParcel(AParcel* parcel) const {
         RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             int32_t size = AParcel_getDataSize(this->mParcel.get());
             RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
         } else {
             return STATUS_INVALID_OPERATION;
         }
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             int32_t size = AParcel_getDataSize(this->mParcel.get());
             RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
         } else {
@@ -79,7 +91,11 @@
     }
 
     binder_status_t readFromParcel(const AParcel* parcel) {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         } else {
             return STATUS_INVALID_OPERATION;
@@ -99,7 +115,11 @@
             return STATUS_BAD_VALUE;
         }
 
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
         } else {
             status = STATUS_INVALID_OPERATION;
@@ -115,7 +135,11 @@
         if (this->mStability > T::_aidl_stability) {
             return STATUS_BAD_VALUE;
         }
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         } else {
             return STATUS_INVALID_OPERATION;
@@ -129,7 +153,11 @@
     binder_status_t getParcelable(std::optional<T>* ret) const {
         const std::string parcelableDesc(T::descriptor);
         AParcel_setDataPosition(mParcel.get(), 0);
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             if (AParcel_getDataSize(mParcel.get()) == 0) {
                 *ret = std::nullopt;
                 return STATUS_OK;
@@ -153,7 +181,11 @@
     }
 
     void reset() {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         }
     }
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 2a66941..dfa8ea2 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -53,11 +53,19 @@
 /**
  * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
  * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
- * for calling AIBinder_decStrong).
+ * for calling AIBinder_decStrong). This does polling. A more efficient way to make sure you
+ * unblock as soon as the service is available is to use AIBinder_waitForService.
  *
  * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
+ * WARNING: when using this API, typically, you should call it in a loop. It's dangerous to
+ * assume that nullptr could mean that the service is not available. The service could just
+ * be starting. Generally, whether a service exists, this information should be declared
+ * externally (for instance, an Android feature might imply the existence of a service,
+ * a system property, or in the case of services in the VINTF manifest, it can be checked
+ * with AServiceManager_isDeclared).
+ *
  * \param instance identifier of the service used to lookup the service.
  */
 __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 7c5afde..c9d6af0 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -924,9 +924,9 @@
             BinderFeatures::default(),
         );
 
-        assert!(!(service1 < service1));
-        assert!(!(service1 > service1));
-        assert_eq!(service1 < service2, !(service2 < service1));
+        assert!((service1 >= service1));
+        assert!((service1 <= service1));
+        assert_eq!(service1 < service2, (service2 >= service1));
     }
 
     #[test]
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 700940a..b1e17b7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -57,6 +57,7 @@
 using android::base::testing::HasValue;
 using android::base::testing::Ok;
 using testing::ExplainMatchResult;
+using testing::Matcher;
 using testing::Not;
 using testing::WithParamInterface;
 
@@ -1258,12 +1259,23 @@
 
 class BinderLibRpcTest : public BinderLibRpcTestBase {};
 
+// e.g. EXPECT_THAT(expr, Debuggable(StatusEq(...))
+// If device is debuggable AND not on user builds, expects matcher.
+// Otherwise expects INVALID_OPERATION.
+// Debuggable + non user builds is necessary but not sufficient for setRpcClientDebug to work.
+static Matcher<status_t> Debuggable(const Matcher<status_t> &matcher) {
+    bool isDebuggable = android::base::GetBoolProperty("ro.debuggable", false) &&
+            android::base::GetProperty("ro.build.type", "") != "user";
+    return isDebuggable ? matcher : StatusEq(INVALID_OPERATION);
+}
+
 TEST_F(BinderLibRpcTest, SetRpcClientDebug) {
     auto binder = addServer();
     ASSERT_TRUE(binder != nullptr);
     auto [socket, port] = CreateSocket();
     ASSERT_TRUE(socket.ok());
-    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), sp<BBinder>::make()), StatusEq(OK));
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), sp<BBinder>::make()),
+                Debuggable(StatusEq(OK)));
 }
 
 // Tests for multiple RpcServer's on the same binder object.
@@ -1274,12 +1286,14 @@
     auto [socket1, port1] = CreateSocket();
     ASSERT_TRUE(socket1.ok());
     auto keepAliveBinder1 = sp<BBinder>::make();
-    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), keepAliveBinder1), StatusEq(OK));
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), keepAliveBinder1),
+                Debuggable(StatusEq(OK)));
 
     auto [socket2, port2] = CreateSocket();
     ASSERT_TRUE(socket2.ok());
     auto keepAliveBinder2 = sp<BBinder>::make();
-    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), keepAliveBinder2), StatusEq(OK));
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), keepAliveBinder2),
+                Debuggable(StatusEq(OK)));
 }
 
 // Negative tests for RPC APIs on IBinder. Call should fail in the same way on both remote and
@@ -1298,7 +1312,7 @@
     auto binder = GetService();
     ASSERT_TRUE(binder != nullptr);
     EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), sp<BBinder>::make()),
-                StatusEq(BAD_VALUE));
+                Debuggable(StatusEq(BAD_VALUE)));
 }
 
 TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoKeepAliveBinder) {
@@ -1306,7 +1320,8 @@
     ASSERT_TRUE(binder != nullptr);
     auto [socket, port] = CreateSocket();
     ASSERT_TRUE(socket.ok());
-    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), nullptr), StatusEq(UNEXPECTED_NULL));
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), nullptr),
+                Debuggable(StatusEq(UNEXPECTED_NULL)));
 }
 INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcTestP, testing::Bool(),
                         BinderLibRpcTestP::ParamToString);
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 1513eca..45a6d47 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -31,6 +31,7 @@
 #include <android-base/unique_fd.h>
 #include <bpf/BpfMap.h>
 #include <cputimeinstate.h>
+#include <cutils/android_filesystem_config.h>
 #include <libbpf.h>
 
 namespace android {
@@ -219,6 +220,7 @@
         uint32_t totalFreqsCount = totalTimes.size();
         std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
         for (auto const &[uid, uidTimes]: *allUid) {
+            if (uid == AID_SDK_SANDBOX) continue;
             for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
                 allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
             }
@@ -646,5 +648,55 @@
     }
 }
 
+void *forceSwitchWithUid(void *uidPtr) {
+    if (!uidPtr) return nullptr;
+    setuid(*(uint32_t *)uidPtr);
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep(&ts, NULL);
+    return nullptr;
+}
+
+TEST_F(TimeInStateTest, SdkSandboxUid) {
+    // Find an unused app UID and its corresponding SDK sandbox uid.
+    uint32_t appUid = AID_APP_START, sandboxUid;
+    {
+        auto times = getUidsCpuFreqTimes();
+        ASSERT_TRUE(times.has_value());
+        ASSERT_FALSE(times->empty());
+        for (const auto &kv : *times) {
+            if (kv.first > AID_APP_END) break;
+            appUid = std::max(appUid, kv.first);
+        }
+        appUid++;
+        sandboxUid = appUid + (AID_SDK_SANDBOX_PROCESS_START - AID_APP_START);
+    }
+
+    // Create a thread to run with the fake sandbox uid.
+    pthread_t thread;
+    ASSERT_EQ(pthread_create(&thread, NULL, &forceSwitchWithUid, &sandboxUid), 0);
+    pthread_join(thread, NULL);
+
+    // Confirm we recorded stats for appUid and AID_SDK_SANDBOX but not sandboxUid
+    auto allTimes = getUidsCpuFreqTimes();
+    ASSERT_TRUE(allTimes.has_value());
+    ASSERT_FALSE(allTimes->empty());
+    ASSERT_NE(allTimes->find(appUid), allTimes->end());
+    ASSERT_NE(allTimes->find(AID_SDK_SANDBOX), allTimes->end());
+    ASSERT_EQ(allTimes->find(sandboxUid), allTimes->end());
+
+    auto allConcurrentTimes = getUidsConcurrentTimes();
+    ASSERT_TRUE(allConcurrentTimes.has_value());
+    ASSERT_FALSE(allConcurrentTimes->empty());
+    ASSERT_NE(allConcurrentTimes->find(appUid), allConcurrentTimes->end());
+    ASSERT_NE(allConcurrentTimes->find(AID_SDK_SANDBOX), allConcurrentTimes->end());
+    ASSERT_EQ(allConcurrentTimes->find(sandboxUid), allConcurrentTimes->end());
+
+    ASSERT_TRUE(clearUidTimes(appUid));
+}
+
 } // namespace bpf
 } // namespace android
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7ce72ff..ec4c7c1 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,12 +132,14 @@
     }
 }
 
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
       : mSurfaceControl(nullptr),
         mSize(1, 1),
         mRequestedSize(mSize),
         mFormat(PIXEL_FORMAT_RGBA_8888),
-        mSyncTransaction(nullptr) {
+        mTransactionReadyCallback(nullptr),
+        mSyncTransaction(nullptr),
+        mUpdateDestinationFrame(updateDestinationFrame) {
     createBufferQueue(&mProducer, &mConsumer);
     // since the adapter is in the client process, set dequeue timeout
     // explicitly so that dequeueBuffer will block
@@ -180,11 +182,12 @@
              static_cast<uint32_t>(mPendingTransactions.size()));
     SurfaceComposerClient::Transaction t;
     mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
-    t.setApplyToken(mApplyToken).apply();
+    // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+    t.setApplyToken(mApplyToken).apply(false, true);
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
-                              int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
+                              int32_t format) {
     LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
 
     std::unique_lock _lock{mMutex};
@@ -193,7 +196,6 @@
         mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
     }
 
-    SurfaceComposerClient::Transaction t;
     const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
     if (surfaceControlChanged && mSurfaceControl != nullptr) {
         BQA_LOGD("Updating SurfaceControl without recreating BBQ");
@@ -203,6 +205,7 @@
     // Always update the native object even though they might have the same layer handle, so we can
     // get the updated transform hint from WM.
     mSurfaceControl = surface;
+    SurfaceComposerClient::Transaction t;
     if (surfaceControlChanged) {
         t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
                    layer_state_t::eEnableBackpressure);
@@ -221,16 +224,15 @@
             // If the buffer supports scaling, update the frame immediately since the client may
             // want to scale the existing buffer to the new size.
             mSize = mRequestedSize;
-            SurfaceComposerClient::Transaction* destFrameTransaction =
-                    (outTransaction) ? outTransaction : &t;
-            destFrameTransaction->setDestinationFrame(mSurfaceControl,
-                                                      Rect(0, 0, newSize.getWidth(),
-                                                           newSize.getHeight()));
-            applyTransaction = true;
+            if (mUpdateDestinationFrame) {
+                t.setDestinationFrame(mSurfaceControl, Rect(newSize));
+                applyTransaction = true;
+            }
         }
     }
     if (applyTransaction) {
-        t.setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t.setApplyToken(mApplyToken).apply(false, true);
     }
 }
 
@@ -498,7 +500,6 @@
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
-    const bool updateDestinationFrame = mRequestedSize != mSize;
     mSize = mRequestedSize;
     Rect crop = computeCrop(bufferItem);
     mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
@@ -517,12 +518,19 @@
 
     mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
-    if (updateDestinationFrame) {
-        t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+    if (mUpdateDestinationFrame) {
+        t->setDestinationFrame(mSurfaceControl, Rect(mSize));
+    } else {
+        const bool ignoreDestinationFrame =
+                bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE;
+        t->setFlags(mSurfaceControl,
+                    ignoreDestinationFrame ? layer_state_t::eIgnoreDestinationFrame : 0,
+                    layer_state_t::eIgnoreDestinationFrame);
     }
     t->setBufferCrop(mSurfaceControl, crop);
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+    t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
     if (!bufferItem.mIsAutoTimestamp) {
         t->setDesiredPresentTime(bufferItem.mTimestamp);
     }
@@ -532,10 +540,6 @@
         mNextFrameTimelineInfoQueue.pop();
     }
 
-    if (mAutoRefresh != bufferItem.mAutoRefresh) {
-        t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
-        mAutoRefresh = bufferItem.mAutoRefresh;
-    }
     {
         std::unique_lock _lock{mTimestampMutex};
         auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
@@ -549,7 +553,13 @@
 
     mergePendingTransactions(t, bufferItem.mFrameNumber);
     if (applyTransaction) {
-        t->setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t->setApplyToken(mApplyToken).apply(false, true);
+        mAppliedLastTransaction = true;
+        mLastAppliedFrameNumber = bufferItem.mFrameNumber;
+    } else {
+        t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber);
+        mAppliedLastTransaction = false;
     }
 
     BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -607,60 +617,69 @@
 }
 
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
-    BBQ_TRACE();
-    std::unique_lock _lock{mMutex};
+    std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
+    SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+    {
+        BBQ_TRACE();
+        std::unique_lock _lock{mMutex};
+        const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
+        BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
 
-    const bool syncTransactionSet = mSyncTransaction != nullptr;
-    BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+        if (syncTransactionSet) {
+            bool mayNeedToWaitForBuffer = true;
+            // If we are going to re-use the same mSyncTransaction, release the buffer that may
+            // already be set in the Transaction. This is to allow us a free slot early to continue
+            // processing a new buffer.
+            if (!mAcquireSingleBuffer) {
+                auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+                if (bufferData) {
+                    BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+                             bufferData->frameNumber);
+                    releaseBuffer(bufferData->generateReleaseCallbackId(),
+                                  bufferData->acquireFence);
+                    // Because we just released a buffer, we know there's no need to wait for a free
+                    // buffer.
+                    mayNeedToWaitForBuffer = false;
+                }
+            }
 
-    if (syncTransactionSet) {
-        bool mayNeedToWaitForBuffer = true;
-        // If we are going to re-use the same mSyncTransaction, release the buffer that may already
-        // be set in the Transaction. This is to allow us a free slot early to continue processing
-        // a new buffer.
-        if (!mAcquireSingleBuffer) {
-            auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
-            if (bufferData) {
-                BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
-                         bufferData->frameNumber);
-                releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence);
-                // Because we just released a buffer, we know there's no need to wait for a free
-                // buffer.
-                mayNeedToWaitForBuffer = false;
+            if (mayNeedToWaitForBuffer) {
+                flushAndWaitForFreeBuffer(_lock);
             }
         }
 
-        if (mayNeedToWaitForBuffer) {
-            flushAndWaitForFreeBuffer(_lock);
+        // add to shadow queue
+        mNumFrameAvailable++;
+        if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+            acquireAndReleaseBuffer();
+        }
+        ATRACE_INT(mQueuedBufferTrace.c_str(),
+                   mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
+
+        BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s",
+                 item.mFrameNumber, boolToString(syncTransactionSet));
+
+        if (syncTransactionSet) {
+            acquireNextBufferLocked(mSyncTransaction);
+
+            // Only need a commit callback when syncing to ensure the buffer that's synced has been
+            // sent to SF
+            incStrong((void*)transactionCommittedCallbackThunk);
+            mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+                                                              static_cast<void*>(this));
+            mWaitForTransactionCallback = true;
+            if (mAcquireSingleBuffer) {
+                prevCallback = mTransactionReadyCallback;
+                prevTransaction = mSyncTransaction;
+                mTransactionReadyCallback = nullptr;
+                mSyncTransaction = nullptr;
+            }
+        } else if (!mWaitForTransactionCallback) {
+            acquireNextBufferLocked(std::nullopt);
         }
     }
-
-    // add to shadow queue
-    mNumFrameAvailable++;
-    if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
-        acquireAndReleaseBuffer();
-    }
-    ATRACE_INT(mQueuedBufferTrace.c_str(),
-               mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
-
-    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
-             boolToString(syncTransactionSet));
-
-    if (syncTransactionSet) {
-        acquireNextBufferLocked(mSyncTransaction);
-
-        // Only need a commit callback when syncing to ensure the buffer that's synced has been sent
-        // to SF
-        incStrong((void*)transactionCommittedCallbackThunk);
-        mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
-                                                          static_cast<void*>(this));
-
-        if (mAcquireSingleBuffer) {
-            mSyncTransaction = nullptr;
-        }
-        mWaitForTransactionCallback = true;
-    } else if (!mWaitForTransactionCallback) {
-        acquireNextBufferLocked(std::nullopt);
+    if (prevCallback) {
+        prevCallback(prevTransaction);
     }
 }
 
@@ -679,12 +698,37 @@
     mDequeueTimestamps.erase(bufferId);
 };
 
-void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
-                                          bool acquireSingleBuffer) {
+void BLASTBufferQueue::syncNextTransaction(
+        std::function<void(SurfaceComposerClient::Transaction*)> callback,
+        bool acquireSingleBuffer) {
     BBQ_TRACE();
     std::lock_guard _lock{mMutex};
-    mSyncTransaction = t;
-    mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
+    mTransactionReadyCallback = callback;
+    if (callback) {
+        mSyncTransaction = new SurfaceComposerClient::Transaction();
+    } else {
+        mSyncTransaction = nullptr;
+    }
+    mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true;
+}
+
+void BLASTBufferQueue::stopContinuousSyncTransaction() {
+    std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
+    SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+    {
+        std::lock_guard _lock{mMutex};
+        bool invokeCallback = mTransactionReadyCallback && !mAcquireSingleBuffer;
+        if (invokeCallback) {
+            prevCallback = mTransactionReadyCallback;
+            prevTransaction = mSyncTransaction;
+        }
+        mTransactionReadyCallback = nullptr;
+        mSyncTransaction = nullptr;
+        mAcquireSingleBuffer = true;
+    }
+    if (prevCallback) {
+        prevCallback(prevTransaction);
+    }
 }
 
 bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
@@ -821,7 +865,8 @@
 
     SurfaceComposerClient::Transaction t;
     mergePendingTransactions(&t, frameNumber);
-    t.setApplyToken(mApplyToken).apply();
+    // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+    t.setApplyToken(mApplyToken).apply(false, true);
 }
 
 void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
@@ -1014,7 +1059,8 @@
                  static_cast<uint32_t>(mPendingTransactions.size()));
         SurfaceComposerClient::Transaction t;
         mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
-        t.setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t.setApplyToken(mApplyToken).apply(false, true);
     }
 
     // Clear sync states
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 75c5e26..5532c6e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -46,9 +46,11 @@
 
 namespace android {
 
+using gui::DisplayCaptureArgs;
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
 using gui::IWindowInfosListener;
+using gui::LayerCaptureArgs;
 using ui::ColorMode;
 
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
@@ -109,7 +111,13 @@
 
         SAFE_PARCEL(data.writeUint64, transactionId);
 
-        return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+        if (flags & ISurfaceComposer::eOneWay) {
+            return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+                    data, &reply, IBinder::FLAG_ONEWAY);
+        } else {
+            return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+                    data, &reply);
+        }
     }
 
     void bootFinished() override {
@@ -118,36 +126,6 @@
         remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
     }
 
-    status_t captureDisplay(const DisplayCaptureArgs& args,
-                            const sp<IScreenCaptureListener>& captureListener) override {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(args.write, data);
-        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
-        return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
-    }
-
-    status_t captureDisplay(DisplayId displayId,
-                            const sp<IScreenCaptureListener>& captureListener) override {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(data.writeUint64, displayId.value);
-        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
-        return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
-    }
-
-    status_t captureLayers(const LayerCaptureArgs& args,
-                           const sp<IScreenCaptureListener>& captureListener) override {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(args.write, data);
-        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
-
-        return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
-    }
-
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& bufferProducer) const override {
         Parcel data, reply;
@@ -1451,36 +1429,6 @@
             bootFinished();
             return NO_ERROR;
         }
-        case CAPTURE_DISPLAY: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            DisplayCaptureArgs args;
-            sp<IScreenCaptureListener> captureListener;
-            SAFE_PARCEL(args.read, data);
-            SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
-            return captureDisplay(args, captureListener);
-        }
-        case CAPTURE_DISPLAY_BY_ID: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            uint64_t value;
-            SAFE_PARCEL(data.readUint64, &value);
-            const auto id = DisplayId::fromValue(value);
-            if (!id) return BAD_VALUE;
-
-            sp<IScreenCaptureListener> captureListener;
-            SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
-            return captureDisplay(*id, captureListener);
-        }
-        case CAPTURE_LAYERS: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            LayerCaptureArgs args;
-            sp<IScreenCaptureListener> captureListener;
-            SAFE_PARCEL(args.read, data);
-            SAFE_PARCEL(data.readStrongBinder, &captureListener);
-
-            return captureLayers(args, captureListener);
-        }
         case AUTHENTICATE_SURFACE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IGraphicBufferProducer> bufferProducer =
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9022e7d..338ff11 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -686,85 +686,89 @@
 
 // ----------------------------------------------------------------------------
 
-status_t CaptureArgs::write(Parcel& output) const {
-    SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat));
-    SAFE_PARCEL(output.write, sourceCrop);
-    SAFE_PARCEL(output.writeFloat, frameScaleX);
-    SAFE_PARCEL(output.writeFloat, frameScaleY);
-    SAFE_PARCEL(output.writeBool, captureSecureLayers);
-    SAFE_PARCEL(output.writeInt32, uid);
-    SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
-    SAFE_PARCEL(output.writeBool, allowProtected);
-    SAFE_PARCEL(output.writeBool, grayscale);
+namespace gui {
+
+status_t CaptureArgs::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(pixelFormat));
+    SAFE_PARCEL(output->write, sourceCrop);
+    SAFE_PARCEL(output->writeFloat, frameScaleX);
+    SAFE_PARCEL(output->writeFloat, frameScaleY);
+    SAFE_PARCEL(output->writeBool, captureSecureLayers);
+    SAFE_PARCEL(output->writeInt32, uid);
+    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
+    SAFE_PARCEL(output->writeBool, allowProtected);
+    SAFE_PARCEL(output->writeBool, grayscale);
     return NO_ERROR;
 }
 
-status_t CaptureArgs::read(const Parcel& input) {
+status_t CaptureArgs::readFromParcel(const Parcel* input) {
     int32_t value = 0;
-    SAFE_PARCEL(input.readInt32, &value);
+    SAFE_PARCEL(input->readInt32, &value);
     pixelFormat = static_cast<ui::PixelFormat>(value);
-    SAFE_PARCEL(input.read, sourceCrop);
-    SAFE_PARCEL(input.readFloat, &frameScaleX);
-    SAFE_PARCEL(input.readFloat, &frameScaleY);
-    SAFE_PARCEL(input.readBool, &captureSecureLayers);
-    SAFE_PARCEL(input.readInt32, &uid);
-    SAFE_PARCEL(input.readInt32, &value);
+    SAFE_PARCEL(input->read, sourceCrop);
+    SAFE_PARCEL(input->readFloat, &frameScaleX);
+    SAFE_PARCEL(input->readFloat, &frameScaleY);
+    SAFE_PARCEL(input->readBool, &captureSecureLayers);
+    SAFE_PARCEL(input->readInt32, &uid);
+    SAFE_PARCEL(input->readInt32, &value);
     dataspace = static_cast<ui::Dataspace>(value);
-    SAFE_PARCEL(input.readBool, &allowProtected);
-    SAFE_PARCEL(input.readBool, &grayscale);
+    SAFE_PARCEL(input->readBool, &allowProtected);
+    SAFE_PARCEL(input->readBool, &grayscale);
     return NO_ERROR;
 }
 
-status_t DisplayCaptureArgs::write(Parcel& output) const {
-    SAFE_PARCEL(CaptureArgs::write, output);
+status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(CaptureArgs::writeToParcel, output);
 
-    SAFE_PARCEL(output.writeStrongBinder, displayToken);
-    SAFE_PARCEL(output.writeUint32, width);
-    SAFE_PARCEL(output.writeUint32, height);
-    SAFE_PARCEL(output.writeBool, useIdentityTransform);
+    SAFE_PARCEL(output->writeStrongBinder, displayToken);
+    SAFE_PARCEL(output->writeUint32, width);
+    SAFE_PARCEL(output->writeUint32, height);
+    SAFE_PARCEL(output->writeBool, useIdentityTransform);
     return NO_ERROR;
 }
 
-status_t DisplayCaptureArgs::read(const Parcel& input) {
-    SAFE_PARCEL(CaptureArgs::read, input);
+status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) {
+    SAFE_PARCEL(CaptureArgs::readFromParcel, input);
 
-    SAFE_PARCEL(input.readStrongBinder, &displayToken);
-    SAFE_PARCEL(input.readUint32, &width);
-    SAFE_PARCEL(input.readUint32, &height);
-    SAFE_PARCEL(input.readBool, &useIdentityTransform);
+    SAFE_PARCEL(input->readStrongBinder, &displayToken);
+    SAFE_PARCEL(input->readUint32, &width);
+    SAFE_PARCEL(input->readUint32, &height);
+    SAFE_PARCEL(input->readBool, &useIdentityTransform);
     return NO_ERROR;
 }
 
-status_t LayerCaptureArgs::write(Parcel& output) const {
-    SAFE_PARCEL(CaptureArgs::write, output);
+status_t LayerCaptureArgs::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(CaptureArgs::writeToParcel, output);
 
-    SAFE_PARCEL(output.writeStrongBinder, layerHandle);
-    SAFE_PARCEL(output.writeInt32, excludeHandles.size());
+    SAFE_PARCEL(output->writeStrongBinder, layerHandle);
+    SAFE_PARCEL(output->writeInt32, excludeHandles.size());
     for (auto el : excludeHandles) {
-        SAFE_PARCEL(output.writeStrongBinder, el);
+        SAFE_PARCEL(output->writeStrongBinder, el);
     }
-    SAFE_PARCEL(output.writeBool, childrenOnly);
+    SAFE_PARCEL(output->writeBool, childrenOnly);
     return NO_ERROR;
 }
 
-status_t LayerCaptureArgs::read(const Parcel& input) {
-    SAFE_PARCEL(CaptureArgs::read, input);
+status_t LayerCaptureArgs::readFromParcel(const Parcel* input) {
+    SAFE_PARCEL(CaptureArgs::readFromParcel, input);
 
-    SAFE_PARCEL(input.readStrongBinder, &layerHandle);
+    SAFE_PARCEL(input->readStrongBinder, &layerHandle);
 
     int32_t numExcludeHandles = 0;
-    SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize());
+    SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
     excludeHandles.reserve(numExcludeHandles);
     for (int i = 0; i < numExcludeHandles; i++) {
         sp<IBinder> binder;
-        SAFE_PARCEL(input.readStrongBinder, &binder);
+        SAFE_PARCEL(input->readStrongBinder, &binder);
         excludeHandles.emplace(binder);
     }
 
-    SAFE_PARCEL(input.readBool, &childrenOnly);
+    SAFE_PARCEL(input->readBool, &childrenOnly);
     return NO_ERROR;
 }
 
+}; // namespace gui
+
 ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
     return {buffer->getId(), frameNumber};
 }
@@ -792,6 +796,8 @@
 
     SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
     SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
+    SAFE_PARCEL(output->writeBool, hasBarrier);
+    SAFE_PARCEL(output->writeUint64, barrierFrameNumber);
 
     return NO_ERROR;
 }
@@ -828,6 +834,9 @@
     cachedBuffer.token = tmpBinder;
     SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
 
+    SAFE_PARCEL(input->readBool, &hasBarrier);
+    SAFE_PARCEL(input->readUint64, &barrierFrameNumber);
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9269c3e..27856ce 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -46,6 +46,7 @@
 #include <ui/DynamicDisplayInfo.h>
 
 #include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
 
 // This server size should always be smaller than the server cache size
 #define BUFFER_CACHE_MAX_SIZE 64
@@ -62,6 +63,7 @@
 // ---------------------------------------------------------------------------
 
 ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+ANDROID_SINGLETON_STATIC_INSTANCE(ComposerServiceAIDL);
 
 namespace {
 // Initialize transaction id counter used to generate transaction ids
@@ -120,6 +122,52 @@
     mDeathObserver = nullptr;
 }
 
+ComposerServiceAIDL::ComposerServiceAIDL() : Singleton<ComposerServiceAIDL>() {
+    std::scoped_lock lock(mMutex);
+    connectLocked();
+}
+
+bool ComposerServiceAIDL::connectLocked() {
+    const String16 name("SurfaceFlingerAIDL");
+    mComposerService = waitForService<gui::ISurfaceComposer>(name);
+    if (mComposerService == nullptr) {
+        return false; // fatal error or permission problem
+    }
+
+    // Create the death listener.
+    class DeathObserver : public IBinder::DeathRecipient {
+        ComposerServiceAIDL& mComposerService;
+        virtual void binderDied(const wp<IBinder>& who) {
+            ALOGW("ComposerService aidl remote (surfaceflinger) died [%p]", who.unsafe_get());
+            mComposerService.composerServiceDied();
+        }
+
+    public:
+        explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {}
+    };
+
+    mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this));
+    IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
+    return true;
+}
+
+/*static*/ sp<gui::ISurfaceComposer> ComposerServiceAIDL::getComposerService() {
+    ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
+    std::scoped_lock lock(instance.mMutex);
+    if (instance.mComposerService == nullptr) {
+        if (ComposerServiceAIDL::getInstance().connectLocked()) {
+            ALOGD("ComposerServiceAIDL reconnected");
+        }
+    }
+    return instance.mComposerService;
+}
+
+void ComposerServiceAIDL::composerServiceDied() {
+    std::scoped_lock lock(mMutex);
+    mComposerService = nullptr;
+    mDeathObserver = nullptr;
+}
+
 class DefaultComposerClient: public Singleton<DefaultComposerClient> {
     Mutex mLock;
     sp<SurfaceComposerClient> mClient;
@@ -882,7 +930,7 @@
     }
 }
 
-status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
+status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
     if (mStatus != NO_ERROR) {
         return mStatus;
     }
@@ -936,6 +984,14 @@
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
+    if (oneWay) {
+      if (mForceSynchronous) {
+          ALOGE("Transaction attempted to set synchronous and one way at the same time"
+                " this is an invalid request. Synchronous will win for safety");
+      } else {
+          flags |= ISurfaceComposer::eOneWay;
+      }
+    }
 
     // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
     // it is equivalent for none
@@ -1351,6 +1407,18 @@
     return bufferData;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferHasBarrier(
+        const sp<SurfaceControl>& sc, uint64_t barrierFrameNumber) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->bufferData->hasBarrier = true;
+    s->bufferData->barrierFrameNumber = barrierFrameNumber;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
         const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
         const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
@@ -2267,26 +2335,29 @@
 
 status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
                                           const sp<IScreenCaptureListener>& captureListener) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
     if (s == nullptr) return NO_INIT;
 
-    return s->captureDisplay(captureArgs, captureListener);
+    binder::Status status = s->captureDisplay(captureArgs, captureListener);
+    return status.transactionError();
 }
 
 status_t ScreenshotClient::captureDisplay(DisplayId displayId,
                                           const sp<IScreenCaptureListener>& captureListener) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
     if (s == nullptr) return NO_INIT;
 
-    return s->captureDisplay(displayId, captureListener);
+    binder::Status status = s->captureDisplayById(displayId.value, captureListener);
+    return status.transactionError();
 }
 
 status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
                                          const sp<IScreenCaptureListener>& captureListener) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
     if (s == nullptr) return NO_INIT;
 
-    return s->captureLayers(captureArgs, captureListener);
+    binder::Status status = s->captureLayers(captureArgs, captureListener);
+    return status.transactionError();
 }
 
 // ---------------------------------------------------------------------------------
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 80bd638..2312a8c 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -51,11 +51,11 @@
 }
 
 bool WindowInfo::isSpy() const {
-    return inputFeatures.test(Feature::SPY);
+    return inputConfig.test(InputConfig::SPY);
 }
 
 bool WindowInfo::interceptsStylus() const {
-    return inputFeatures.test(Feature::INTERCEPTS_STYLUS);
+    return inputConfig.test(InputConfig::INTERCEPTS_STYLUS);
 }
 
 bool WindowInfo::overlaps(const WindowInfo* other) const {
@@ -73,8 +73,7 @@
             info.touchableRegion.hasSameRects(touchableRegion) &&
             info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
             info.ownerUid == ownerUid && info.packageName == packageName &&
-            info.inputFeatures == inputFeatures && info.inputConfig == inputConfig &&
-            info.displayId == displayId &&
+            info.inputConfig == inputConfig && info.displayId == displayId &&
             info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
             info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType &&
             info.layoutParamsFlags == layoutParamsFlags;
@@ -92,7 +91,6 @@
     parcel->writeInt32(1);
 
     // Ensure that the size of the flags that we use is 32 bits for writing into the parcel.
-    static_assert(sizeof(inputFeatures) == 4u);
     static_assert(sizeof(inputConfig) == 4u);
 
     // clang-format off
@@ -120,7 +118,6 @@
         parcel->writeInt32(ownerPid) ?:
         parcel->writeInt32(ownerUid) ?:
         parcel->writeUtf8AsUtf16(packageName) ?:
-        parcel->writeInt32(inputFeatures.get()) ?:
         parcel->writeInt32(inputConfig.get()) ?:
         parcel->writeInt32(displayId) ?:
         applicationInfo.writeToParcel(parcel) ?:
@@ -148,12 +145,14 @@
         return status;
     }
 
-    layoutParamsFlags = Flags<Flag>(parcel->readInt32());
-    layoutParamsType = static_cast<Type>(parcel->readInt32());
     float dsdx, dtdx, tx, dtdy, dsdy, ty;
-    int32_t touchOcclusionModeInt;
+    int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt;
+    sp<IBinder> touchableRegionCropHandleSp;
+
     // clang-format off
-    status = parcel->readInt32(&frameLeft) ?:
+    status = parcel->readInt32(&lpFlags) ?:
+        parcel->readInt32(&lpType) ?:
+        parcel->readInt32(&frameLeft) ?:
         parcel->readInt32(&frameTop) ?:
         parcel->readInt32(&frameRight) ?:
         parcel->readInt32(&frameBottom) ?:
@@ -169,33 +168,28 @@
         parcel->readInt32(&touchOcclusionModeInt) ?:
         parcel->readInt32(&ownerPid) ?:
         parcel->readInt32(&ownerUid) ?:
-        parcel->readUtf8FromUtf16(&packageName);
-    // clang-format on
-
-    if (status != OK) {
-        return status;
-    }
-
-    touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
-
-    inputFeatures = Flags<Feature>(parcel->readInt32());
-    inputConfig = Flags<InputConfig>(parcel->readInt32());
-    // clang-format off
-    status = parcel->readInt32(&displayId) ?:
+        parcel->readUtf8FromUtf16(&packageName) ?:
+        parcel->readInt32(&inputConfigInt) ?:
+        parcel->readInt32(&displayId) ?:
         applicationInfo.readFromParcel(parcel) ?:
         parcel->read(touchableRegion) ?:
-        parcel->readBool(&replaceTouchableRegionWithCrop);
+        parcel->readBool(&replaceTouchableRegionWithCrop) ?:
+        parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
+        parcel->readNullableStrongBinder(&windowToken);
     // clang-format on
 
     if (status != OK) {
         return status;
     }
 
-    touchableRegionCropHandle = parcel->readStrongBinder();
+    layoutParamsFlags = Flags<Flag>(lpFlags);
+    layoutParamsType = static_cast<Type>(lpType);
     transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+    touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+    inputConfig = Flags<InputConfig>(inputConfigInt);
+    touchableRegionCropHandle = touchableRegionCropHandleSp;
 
-    status = parcel->readNullableStrongBinder(&windowToken);
-    return status;
+    return OK;
 }
 
 // --- WindowInfoHandle ---
diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
new file mode 100644
index 0000000..2caa2b9
--- /dev/null
+++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
new file mode 100644
index 0000000..07921a5
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.DisplayCaptureArgs;
+import android.gui.LayerCaptureArgs;
+import android.gui.IScreenCaptureListener;
+
+/** @hide */
+interface ISurfaceComposer {
+    /**
+     * Capture the specified screen. This requires READ_FRAME_BUFFER
+     * permission.  This function will fail if there is a secure window on
+     * screen and DisplayCaptureArgs.captureSecureLayers is false.
+     *
+     * This function can capture a subregion (the source crop) of the screen.
+     * The subregion can be optionally rotated.  It will also be scaled to
+     * match the size of the output buffer.
+     */
+    void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
+    void captureDisplayById(long displayId, IScreenCaptureListener listener);
+    /**
+     * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+     * This requires READ_FRAME_BUFFER permission. This function will fail if there
+     * is a secure window on screen
+     */
+    void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);
+}
diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
new file mode 100644
index 0000000..f0def50
--- /dev/null
+++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h";
diff --git a/libs/gui/android/gui/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl
index 9018635..b13c600 100644
--- a/libs/gui/android/gui/FocusRequest.aidl
+++ b/libs/gui/android/gui/FocusRequest.aidl
@@ -21,7 +21,7 @@
     /**
      * Input channel token used to identify the window that should gain focus.
      */
-    IBinder token;
+    @nullable IBinder token;
     @utf8InCpp String windowName;
     /**
      * The token that the caller expects currently to be focused. If the
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1ed592b..65fc04d 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -73,7 +73,7 @@
     : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
 {
 public:
-    BLASTBufferQueue(const std::string& name);
+    BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
     BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
                      int height, int32_t format);
 
@@ -95,13 +95,14 @@
                                      const std::vector<SurfaceControlStats>& stats);
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
                                std::optional<uint32_t> currentMaxAcquiredBufferCount);
-    void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
+    void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
+                             bool acquireSingleBuffer = true);
+    void stopContinuousSyncTransaction();
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void applyPendingTransactions(uint64_t frameNumber);
     SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
 
-    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
-                SurfaceComposerClient::Transaction* outTransaction = nullptr);
+    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -214,15 +215,12 @@
     sp<IGraphicBufferProducer> mProducer;
     sp<BLASTBufferItemConsumer> mBufferItemConsumer;
 
+    std::function<void(SurfaceComposerClient::Transaction*)> mTransactionReadyCallback
+            GUARDED_BY(mMutex);
     SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
-    // Last requested auto refresh state set by the producer. The state indicates that the consumer
-    // should acquire the next frame as soon as it can and not wait for a frame to become available.
-    // This is only relevant for shared buffer mode.
-    bool mAutoRefresh GUARDED_BY(mMutex) = false;
-
     std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
 
     // Tracks the last acquired frame number
@@ -250,6 +248,27 @@
     // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
     // continue to acquire buffers until explicitly cleared
     bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
+
+    // True if BBQ will update the destination frame used to scale the buffer to the requested size.
+    // If false, the caller is responsible for updating the destination frame on the BBQ
+    // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with
+    // additional scales in the hierarchy.
+    bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true;
+
+    // We send all transactions on our apply token over one-way binder calls to avoid blocking
+    // client threads. All of our transactions remain in order, since they are one-way binder calls
+    // from a single process, to a single interface. However once we give up a Transaction for sync
+    // we can start to have ordering issues. When we return from sync to normal frame production,
+    // we wait on the commit callback of sync frames ensuring ordering, however we don't want to
+    // wait on the commit callback for every normal frame (since even emitting them has a
+    // performance cost) this means we need a method to ensure frames are in order when switching
+    // from one-way application on our apply token, to application on some other apply token. We
+    // make use of setBufferHasBarrier to declare this ordering. This boolean simply tracks when we
+    // need to set this flag, notably only in the case where we are transitioning from a previous
+    // transaction applied by us (one way, may not yet have reached server) and an upcoming
+    // transaction that will be applied by some sync consumer.
+    bool mAppliedLastTransaction = false;
+    uint64_t mLastAppliedFrameNumber = 0;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
new file mode 100644
index 0000000..ec884cf
--- /dev/null
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
+namespace android::gui {
+
+struct CaptureArgs : public Parcelable {
+    const static int32_t UNSET_UID = -1;
+    virtual ~CaptureArgs() = default;
+
+    ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
+    Rect sourceCrop;
+    float frameScaleX{1};
+    float frameScaleY{1};
+    bool captureSecureLayers{false};
+    int32_t uid{UNSET_UID};
+    // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+    // result will be in the display's colorspace.
+    // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+    // different from SRGB (byte per color), and failed when checking colors in tests.
+    // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+    ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+
+    // The receiver of the capture can handle protected buffer. A protected buffer has
+    // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+    // Any read/write access from unprotected context will result in undefined behaviour.
+    // Protected contents are typically DRM contents. This has no direct implication to the
+    // secure property of the surface, which is specified by the application explicitly to avoid
+    // the contents being accessed/captured by screenshot or unsecure display.
+    bool allowProtected = false;
+
+    bool grayscale = false;
+
+    virtual status_t writeToParcel(Parcel* output) const;
+    virtual status_t readFromParcel(const Parcel* input);
+};
+
+struct DisplayCaptureArgs : CaptureArgs {
+    sp<IBinder> displayToken;
+    uint32_t width{0};
+    uint32_t height{0};
+    bool useIdentityTransform{false};
+
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+};
+
+}; // namespace android::gui
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0a59f52..0a3cc19 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -58,11 +58,9 @@
 
 struct client_cache_t;
 struct ComposerState;
-struct DisplayCaptureArgs;
 struct DisplayStatInfo;
 struct DisplayState;
 struct InputWindowCommands;
-struct LayerCaptureArgs;
 class LayerDebugInfo;
 class HdrCapabilities;
 class IGraphicBufferProducer;
@@ -75,6 +73,13 @@
 using gui::IScreenCaptureListener;
 using gui::SpHash;
 
+namespace gui {
+
+struct DisplayCaptureArgs;
+struct LayerCaptureArgs;
+
+} // namespace gui
+
 namespace ui {
 
 struct DisplayMode;
@@ -108,6 +113,7 @@
         // android.permission.ACCESS_SURFACE_FLINGER
         eEarlyWakeupStart = 0x08,
         eEarlyWakeupEnd = 0x10,
+        eOneWay = 0x20
     };
 
     enum VsyncSource {
@@ -261,27 +267,6 @@
      */
     virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
 
-    /**
-     * Capture the specified screen. This requires READ_FRAME_BUFFER
-     * permission.  This function will fail if there is a secure window on
-     * screen and DisplayCaptureArgs.captureSecureLayers is false.
-     *
-     * This function can capture a subregion (the source crop) of the screen.
-     * The subregion can be optionally rotated.  It will also be scaled to
-     * match the size of the output buffer.
-     */
-    virtual status_t captureDisplay(const DisplayCaptureArgs&,
-                                    const sp<IScreenCaptureListener>&) = 0;
-
-    virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
-
-    /**
-     * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
-     * This requires READ_FRAME_BUFFER permission. This function will fail if there
-     * is a secure window on screen
-     */
-    virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
-
     /* Clears the frame statistics for animations.
      *
      * Requires the ACCESS_SURFACE_FLINGER permission.
@@ -621,8 +606,8 @@
         GET_DISPLAY_MODES,       // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
         GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
         GET_DISPLAY_STATE,
-        CAPTURE_DISPLAY,
-        CAPTURE_LAYERS,
+        CAPTURE_DISPLAY, // Deprecated. Autogenerated by .aidl now.
+        CAPTURE_LAYERS,  // Deprecated. Autogenerated by .aidl now.
         CLEAR_ANIMATION_FRAME_STATS,
         GET_ANIMATION_FRAME_STATS,
         SET_POWER_MODE,
@@ -649,7 +634,7 @@
         GET_DESIRED_DISPLAY_MODE_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
-        CAPTURE_DISPLAY_BY_ID,
+        CAPTURE_DISPLAY_BY_ID, // Deprecated. Autogenerated by .aidl now.
         NOTIFY_POWER_BOOST,
         SET_GLOBAL_SHADOW_SETTINGS,
         GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
diff --git a/libs/gui/include/gui/LayerCaptureArgs.h b/libs/gui/include/gui/LayerCaptureArgs.h
new file mode 100644
index 0000000..05ff9d5
--- /dev/null
+++ b/libs/gui/include/gui/LayerCaptureArgs.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/DisplayCaptureArgs.h>
+#include <gui/SpHash.h>
+#include <unordered_set>
+
+namespace android::gui {
+
+struct LayerCaptureArgs : CaptureArgs {
+    sp<IBinder> layerHandle;
+    std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
+    bool childrenOnly{false};
+
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+};
+
+}; // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f720619..0f37dab 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -29,7 +29,9 @@
 #include <android/gui/DropInputMode.h>
 #include <android/gui/FocusRequest.h>
 
+#include <gui/DisplayCaptureArgs.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/LayerCaptureArgs.h>
 #include <gui/LayerMetadata.h>
 #include <gui/SpHash.h>
 #include <gui/SurfaceControl.h>
@@ -87,6 +89,8 @@
     // Used by BlastBufferQueue to forward the framenumber generated by the
     // graphics producer.
     uint64_t frameNumber = 0;
+    bool hasBarrier = false;
+    uint64_t barrierFrameNumber = 0;
 
     // Listens to when the buffer is safe to be released. This is used for blast
     // layers only. The callback includes a release fence as well as the graphic
@@ -128,8 +132,13 @@
         // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
         // set. This blocks the client until all the buffers have been presented. If the buffers
         // have presentation timestamps, then we may drop buffers.
-        eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
-        eLayerIsDisplayDecoration = 0x200,  // DISPLAY_DECORATION
+        eEnableBackpressure = 0x100,       // ENABLE_BACKPRESSURE
+        eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION
+        // Ignore any destination frame set on the layer. This is used when the buffer scaling mode
+        // is freeze and the destination frame is applied asynchronously with the buffer submission.
+        // This is needed to maintain compatibility for SurfaceView scaling behavior.
+        // See SurfaceView scaling behavior for more details.
+        eIgnoreDestinationFrame = 0x400,
     };
 
     enum {
@@ -370,56 +379,6 @@
 bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
                        const char* functionName, bool privileged = false);
 
-struct CaptureArgs {
-    const static int32_t UNSET_UID = -1;
-    virtual ~CaptureArgs() = default;
-
-    ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
-    Rect sourceCrop;
-    float frameScaleX{1};
-    float frameScaleY{1};
-    bool captureSecureLayers{false};
-    int32_t uid{UNSET_UID};
-    // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
-    // result will be in the display's colorspace.
-    // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
-    // different from SRGB (byte per color), and failed when checking colors in tests.
-    // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
-    ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
-
-    // The receiver of the capture can handle protected buffer. A protected buffer has
-    // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
-    // Any read/write access from unprotected context will result in undefined behaviour.
-    // Protected contents are typically DRM contents. This has no direct implication to the
-    // secure property of the surface, which is specified by the application explicitly to avoid
-    // the contents being accessed/captured by screenshot or unsecure display.
-    bool allowProtected = false;
-
-    bool grayscale = false;
-
-    virtual status_t write(Parcel& output) const;
-    virtual status_t read(const Parcel& input);
-};
-
-struct DisplayCaptureArgs : CaptureArgs {
-    sp<IBinder> displayToken;
-    uint32_t width{0};
-    uint32_t height{0};
-    bool useIdentityTransform{false};
-
-    status_t write(Parcel& output) const override;
-    status_t read(const Parcel& input) override;
-};
-
-struct LayerCaptureArgs : CaptureArgs {
-    sp<IBinder> layerHandle;
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
-    bool childrenOnly{false};
-
-    status_t write(Parcel& output) const override;
-    status_t read(const Parcel& input) override;
-};
-
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 25637ef..c8ac166 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -57,7 +57,9 @@
 class ITunnelModeEnabledListener;
 class Region;
 
+using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
+using gui::LayerCaptureArgs;
 
 struct SurfaceControlStats {
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -459,7 +461,7 @@
         // Clears the contents of the transaction without applying it.
         void clear();
 
-        status_t apply(bool synchronous = false);
+        status_t apply(bool synchronous = false, bool oneWay = false);
         // Merge another transaction in to this one, clearing other
         // as if it had been applied.
         Transaction& merge(Transaction&& other);
@@ -519,6 +521,27 @@
                                const std::optional<uint64_t>& frameNumber = std::nullopt,
                                ReleaseBufferCallback callback = nullptr);
         std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
+
+        /**
+         * If this transaction, has a a buffer set for the given SurfaceControl
+         * mark that buffer as ordered after a given barrierFrameNumber.
+         *
+         * SurfaceFlinger will refuse to apply this transaction until after
+         * the frame in barrierFrameNumber has been applied. This transaction may
+         * be applied in the same frame as the barrier buffer or after.
+         *
+         * This is only designed to be used to handle switches between multiple
+         * apply tokens, as explained in the comment for BLASTBufferQueue::mAppliedLastTransaction.
+         *
+         * Has to be called after setBuffer.
+         *
+         * WARNING:
+         * This API is very dangerous to the caller, as if you invoke it without
+         * a frameNumber you have not yet submitted, you can dead-lock your
+         * SurfaceControl's transaction queue.
+         */
+        Transaction& setBufferHasBarrier(const sp<SurfaceControl>& sc,
+                                         uint64_t barrierFrameNumber);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
         Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b9bffaa..ef0b98b 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <android/gui/TouchOcclusionMode.h>
-#include <android/os/IInputConstants.h>
+#include <android/os/InputConfig.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <ftl/Flags.h>
@@ -132,49 +132,45 @@
         ftl_last = FIRST_SYSTEM_WINDOW + 15
     };
 
-    // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned
-    // type. This indicates that they are flags, so it can be used with ftl/enum.h.
-    enum class Feature : uint32_t {
-        // clang-format off
-        NO_INPUT_CHANNEL =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL),
-        DISABLE_USER_ACTIVITY =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY),
-        DROP_INPUT =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT),
-        DROP_INPUT_IF_OBSCURED =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED),
-        SPY =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY),
-        INTERCEPTS_STYLUS =
-                static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS),
-        // clang-format on
-    };
-
     // Flags used to determine configuration of this input window.
-    // Input windows can be configured with two sets of flags: InputFeature (WindowInfo::Feature
-    // defined above), and InputConfig. When adding a new configuration for an input window:
-    //   - If you are adding a new flag that's visible and accessible to apps, it should be added
-    //   as an InputFeature.
-    //   - If you are adding an internal behaviour that is used within the system or shell and is
-    //   not exposed to apps, it should be added as an InputConfig.
+    // This is a conversion of os::InputConfig to an enum backed by an unsigned
+    // type. This indicates that they are flags, so it can be used with ftl/enum.h.
     enum class InputConfig : uint32_t {
         // clang-format off
-        NONE                         = 0,
-        NOT_VISIBLE                  = 1 << 0,
-        NOT_FOCUSABLE                = 1 << 1,
-        NOT_TOUCHABLE                = 1 << 2,
-        PREVENT_SPLITTING            = 1 << 3,
-        DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 4,
-        IS_WALLPAPER                 = 1 << 5,
-        PAUSE_DISPATCHING            = 1 << 6,
-        // This flag is set when the window is of a trusted type that is allowed to silently
-        // overlay other windows for the purpose of implementing the secure views feature.
-        // Trusted overlays, such as IME windows, can partly obscure other windows without causing
-        // motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
-        TRUSTED_OVERLAY              = 1 << 7,
-        WATCH_OUTSIDE_TOUCH          = 1 << 8,
-        SLIPPERY                     = 1 << 9,
+        DEFAULT =
+                static_cast<uint32_t>(os::InputConfig::DEFAULT),
+        NO_INPUT_CHANNEL =
+                static_cast<uint32_t>(os::InputConfig::NO_INPUT_CHANNEL),
+        NOT_VISIBLE =
+                static_cast<uint32_t>(os::InputConfig::NOT_VISIBLE),
+        NOT_FOCUSABLE =
+                static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE),
+        NOT_TOUCHABLE =
+                static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE),
+        PREVENT_SPLITTING =
+                static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING),
+        DUPLICATE_TOUCH_TO_WALLPAPER =
+                static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER),
+        IS_WALLPAPER =
+                static_cast<uint32_t>(os::InputConfig::IS_WALLPAPER),
+        PAUSE_DISPATCHING =
+                static_cast<uint32_t>(os::InputConfig::PAUSE_DISPATCHING),
+        TRUSTED_OVERLAY =
+                static_cast<uint32_t>(os::InputConfig::TRUSTED_OVERLAY),
+        WATCH_OUTSIDE_TOUCH =
+                static_cast<uint32_t>(os::InputConfig::WATCH_OUTSIDE_TOUCH),
+        SLIPPERY =
+                static_cast<uint32_t>(os::InputConfig::SLIPPERY),
+        DISABLE_USER_ACTIVITY =
+                static_cast<uint32_t>(os::InputConfig::DISABLE_USER_ACTIVITY),
+        DROP_INPUT =
+                static_cast<uint32_t>(os::InputConfig::DROP_INPUT),
+        DROP_INPUT_IF_OBSCURED =
+                static_cast<uint32_t>(os::InputConfig::DROP_INPUT_IF_OBSCURED),
+        SPY =
+                static_cast<uint32_t>(os::InputConfig::SPY),
+        INTERCEPTS_STYLUS =
+                static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS),
         // clang-format on
     };
 
@@ -228,7 +224,6 @@
     int32_t ownerPid = -1;
     int32_t ownerUid = -1;
     std::string packageName;
-    Flags<Feature> inputFeatures;
     Flags<InputConfig> inputConfig;
     int32_t displayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
diff --git a/libs/gui/include/private/gui/ComposerService.h b/libs/gui/include/private/gui/ComposerService.h
index fa1071a..05ed0a0 100644
--- a/libs/gui/include/private/gui/ComposerService.h
+++ b/libs/gui/include/private/gui/ComposerService.h
@@ -37,7 +37,7 @@
 // Users of this class should not retain the value from
 // getComposerService() for an extended period.
 //
-// (It's not clear that using Singleton is useful here anymore.)
+// (TODO: b/219785927, It's not clear that using Singleton is useful here anymore.)
 class ComposerService : public Singleton<ComposerService>
 {
     sp<ISurfaceComposer> mComposerService;
diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h
new file mode 100644
index 0000000..fee37ee
--- /dev/null
+++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <android/gui/ISurfaceComposer.h>
+
+#include <utils/Singleton.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+
+// This holds our connection to the composer service (i.e. SurfaceFlinger).
+// If the remote side goes away, we will re-establish the connection.
+// Users of this class should not retain the value from
+// getComposerService() for an extended period.
+//
+// (TODO: b/219785927, It's not clear that using Singleton is useful here anymore.)
+class ComposerServiceAIDL : public Singleton<ComposerServiceAIDL> {
+    sp<gui::ISurfaceComposer> mComposerService;
+    sp<IBinder::DeathRecipient> mDeathObserver;
+    mutable std::mutex mMutex;
+
+    ComposerServiceAIDL();
+    bool connectLocked();
+    void composerServiceDied();
+    friend class Singleton<ComposerServiceAIDL>;
+
+public:
+    // Get a connection to the Composer Service.  This will block until
+    // a connection is established. Returns null if permission is denied.
+    static sp<gui::ISurfaceComposer> getComposerService();
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 42a32f3..0c3236c 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -29,6 +29,7 @@
 #include <gui/SyncScreenCaptureListener.h>
 #include <gui/test/CallbackUtils.h>
 #include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
 #include <ui/DisplayMode.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
@@ -109,15 +110,27 @@
         mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
     }
 
-    void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
-        mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
+    void setSyncTransaction(Transaction& next, bool acquireSingleBuffer = true) {
+        auto callback = [&next](Transaction* t) { next.merge(std::move(*t)); };
+        mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer);
+    }
+
+    void syncNextTransaction(std::function<void(Transaction*)> callback,
+                             bool acquireSingleBuffer = true) {
+        mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer);
+    }
+
+    void stopContinuousSyncTransaction() {
+        mBlastBufferQueueAdapter->stopContinuousSyncTransaction();
     }
 
     int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
 
     int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
 
-    Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
+    std::function<void(Transaction*)> getTransactionReadyCallback() {
+        return mBlastBufferQueueAdapter->mTransactionReadyCallback;
+    }
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
         return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -283,13 +296,13 @@
 
     static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
                                    ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerService::getComposerService();
+        const auto sf = ComposerServiceAIDL::getComposerService();
         SurfaceComposerClient::Transaction().apply(true);
 
         const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        status_t status = sf->captureDisplay(captureArgs, captureListener);
-        if (status != NO_ERROR) {
-            return status;
+        binder::Status status = sf->captureDisplay(captureArgs, captureListener);
+        if (status.transactionError() != NO_ERROR) {
+            return status.transactionError();
         }
         captureResults = captureListener->waitForResults();
         return captureResults.result;
@@ -342,7 +355,7 @@
     ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
     ASSERT_EQ(mDisplayWidth, adapter.getWidth());
     ASSERT_EQ(mDisplayHeight, adapter.getHeight());
-    ASSERT_EQ(nullptr, adapter.getSyncTransaction());
+    ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
 }
 
 TEST_F(BLASTBufferQueueTest, Update) {
@@ -363,11 +376,12 @@
     ASSERT_EQ(mDisplayHeight / 2, height);
 }
 
-TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
+TEST_F(BLASTBufferQueueTest, SyncNextTransaction) {
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
-    Transaction sync;
-    adapter.setSyncTransaction(&sync);
-    ASSERT_EQ(&sync, adapter.getSyncTransaction());
+    ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
+    auto callback = [](Transaction*) {};
+    adapter.syncNextTransaction(callback);
+    ASSERT_NE(nullptr, adapter.getTransactionReadyCallback());
 }
 
 TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -807,7 +821,7 @@
     setUpProducer(adapter, igbProducer);
 
     Transaction sync;
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     // queue non sync buffer, so this one should get blocked
@@ -847,12 +861,12 @@
     Transaction mainTransaction;
 
     Transaction sync;
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     mainTransaction.merge(std::move(sync));
 
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, r, g, b, 0);
 
     mainTransaction.merge(std::move(sync));
@@ -888,7 +902,7 @@
 
     Transaction sync;
     // queue a sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     mainTransaction.merge(std::move(sync));
@@ -897,7 +911,7 @@
     queueBuffer(igbProducer, 0, 0, 255, 0);
 
     // queue another sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 1 buffer to be released because the non sync transaction should merge
     // with the sync
@@ -936,7 +950,7 @@
 
     Transaction sync;
     // queue a sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     mainTransaction.merge(std::move(sync));
@@ -947,7 +961,7 @@
     queueBuffer(igbProducer, 0, 0, 255, 0);
 
     // queue another sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 3 buffers to be released because the non sync transactions should merge
     // with the sync
@@ -993,7 +1007,7 @@
 
     Transaction sync;
     // queue a sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     mainTransaction.merge(std::move(sync));
@@ -1007,7 +1021,7 @@
     mainTransaction.apply();
 
     // queue another sync transaction
-    adapter.setSyncTransaction(&sync);
+    adapter.setSyncTransaction(sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 2 buffers to be released because the non sync transactions should merge
     // with the sync
@@ -1030,19 +1044,19 @@
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
 
-TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionAcquireMultipleBuffers) {
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
 
     sp<IGraphicBufferProducer> igbProducer;
     setUpProducer(adapter, igbProducer);
 
     Transaction next;
-    adapter.setSyncTransaction(&next, false);
+    adapter.setSyncTransaction(next, false);
     queueBuffer(igbProducer, 0, 255, 0, 0);
     queueBuffer(igbProducer, 0, 0, 255, 0);
     // There should only be one frame submitted since the first frame will be released.
     adapter.validateNumFramesSubmitted(1);
-    adapter.setSyncTransaction(nullptr);
+    adapter.stopContinuousSyncTransaction();
 
     // queue non sync buffer, so this one should get blocked
     // Add a present delay to allow the first screenshot to get taken.
@@ -1096,7 +1110,7 @@
         Transaction next;
         queueBuffer(igbProducer, 0, 255, 0, 0);
         queueBuffer(igbProducer, 0, 0, 255, 0);
-        adapter.setSyncTransaction(&next, false);
+        adapter.setSyncTransaction(next, true);
         queueBuffer(igbProducer, 255, 0, 0, 0);
 
         CallbackHelper transactionCallback;
@@ -1139,7 +1153,7 @@
         Transaction next;
         queueBuffer(igbProducer, 0, 255, 0, 0);
         queueBuffer(igbProducer, 0, 0, 255, 0);
-        adapter.setSyncTransaction(&next, false);
+        adapter.setSyncTransaction(next, true);
         queueBuffer(igbProducer, 255, 0, 0, 0);
 
         CallbackHelper transactionCallback;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6056280..a885e92 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -31,6 +31,7 @@
 #include <gui/SyncScreenCaptureListener.h>
 #include <inttypes.h>
 #include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
 #include <sys/types.h>
 #include <ui/BufferQueueDefs.h>
 #include <ui/DisplayMode.h>
@@ -205,13 +206,13 @@
 
     static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
                                    ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerService::getComposerService();
+        const auto sf = ComposerServiceAIDL::getComposerService();
         SurfaceComposerClient::Transaction().apply(true);
 
         const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        status_t status = sf->captureDisplay(captureArgs, captureListener);
-        if (status != NO_ERROR) {
-            return status;
+        binder::Status status = sf->captureDisplay(captureArgs, captureListener);
+        if (status.transactionError() != NO_ERROR) {
+            return status.transactionError();
         }
         captureResults = captureListener->waitForResults();
         return captureResults.result;
@@ -766,16 +767,6 @@
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
 
-    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
-        return NO_ERROR;
-    }
-    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
-        return NO_ERROR;
-    }
-    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
-        return NO_ERROR;
-    }
-
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
     status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
         return NO_ERROR;
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ff9bae2..c51b244 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -64,7 +64,6 @@
     i.ownerPid = 19;
     i.ownerUid = 24;
     i.packageName = "com.example.package";
-    i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY;
     i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
     i.displayId = 34;
     i.replaceTouchableRegionWithCrop = true;
@@ -97,7 +96,6 @@
     ASSERT_EQ(i.ownerPid, i2.ownerPid);
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
     ASSERT_EQ(i.packageName, i2.packageName);
-    ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.inputConfig, i2.inputConfig);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 930d819..606fe2a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
         "android/os/IInputConstants.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
+        "android/os/InputConfig.aidl",
     ],
 }
 
@@ -79,11 +80,8 @@
         android: {
             srcs: [
                 "InputTransport.cpp",
-                "android/os/BlockUntrustedTouchesMode.aidl",
-                "android/os/IInputConstants.aidl",
                 "android/os/IInputFlinger.aidl",
-                "android/os/InputEventInjectionResult.aidl",
-                "android/os/InputEventInjectionSync.aidl",
+                ":inputconstants_aidl",
             ],
 
             export_shared_lib_headers: ["libbinder"],
@@ -119,6 +117,7 @@
                 "InputTransport.cpp",
                 "android/os/IInputConstants.aidl",
                 "android/os/IInputFlinger.aidl",
+                "android/os/InputConfig.aidl",
             ],
             static_libs: [
                 "libhostgraphics",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3073d94..fe1754c 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -846,6 +846,7 @@
     return calculateTransformedXYUnchecked(source, transform, xy);
 }
 
+// Keep in sync with calculateTransformedCoords.
 float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
                                                  const ui::Transform& transform,
                                                  const PointerCoords& coords) {
@@ -874,6 +875,34 @@
     return coords.getAxisValue(axis);
 }
 
+// Keep in sync with calculateTransformedAxisValue. This is an optimization of
+// calculateTransformedAxisValue for all PointerCoords axes.
+PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
+                                                      const ui::Transform& transform,
+                                                      const PointerCoords& coords) {
+    if (shouldDisregardTransformation(source)) {
+        return coords;
+    }
+    PointerCoords out = coords;
+
+    const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+    out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+    out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+    const vec2 relativeXy =
+            transformWithoutTranslation(transform,
+                                        {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+                                         coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
+    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
+
+    out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+                     transformAngle(transform,
+                                    coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)));
+
+    return out;
+}
+
 // --- FocusEvent ---
 
 void FocusEvent::initialize(int32_t id, bool hasFocus) {
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 265cbf0..5ce10a4 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -47,55 +47,6 @@
      */
     const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
 
-    @Backing(type="int")
-    enum InputFeature {
-        /**
-         * Does not construct an input channel for this window.  The channel will therefore
-         * be incapable of receiving input.
-         */
-        NO_INPUT_CHANNEL = 0x00000002,
-
-        /**
-         * When this window has focus, does not call user activity for all input events so
-         * the application will have to do it itself.  Should only be used by
-         * the keyguard and phone app.
-         *
-         * Should only be used by the keyguard and phone app.
-         */
-        DISABLE_USER_ACTIVITY = 0x00000004,
-
-        /**
-         * Internal flag used to indicate that input should be dropped on this window.
-         */
-        DROP_INPUT = 0x00000008,
-
-        /**
-         * Internal flag used to indicate that input should be dropped on this window if this window
-         * is obscured.
-         */
-        DROP_INPUT_IF_OBSCURED = 0x00000010,
-
-        /**
-         * An input spy window. This window will receive all pointer events within its touchable
-         * area, but will will not stop events from being sent to other windows below it in z-order.
-         * An input event will be dispatched to all spy windows above the top non-spy window at the
-         * event's coordinates.
-         */
-        SPY = 0x00000020,
-
-        /**
-         * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
-         * to receive events from a stylus device within its touchable region. All other pointer
-         * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
-         *
-         * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
-         * not set.
-         *
-         * The window must be a trusted overlay to use this input feature.
-         */
-        INTERCEPTS_STYLUS = 0x00000040,
-    }
-
     /* The default pointer acceleration value. */
     const int DEFAULT_POINTER_ACCELERATION = 3;
 }
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
new file mode 100644
index 0000000..6d1b396
--- /dev/null
+++ b/libs/input/android/os/InputConfig.aidl
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/**
+ * Input configurations flags used to determine the behavior of input windows.
+ * @hide
+ */
+@Backing(type="int")
+enum InputConfig {
+
+    /**
+     * The default InputConfig value with no flags set.
+     */
+    DEFAULT                      = 0,
+
+    /**
+     * Does not construct an input channel for this window.  The channel will therefore
+     * be incapable of receiving input.
+     */
+    NO_INPUT_CHANNEL             = 1 << 0,
+
+    /**
+     * Indicates that this input window is not visible, and thus will not be considered as
+     * an input target and will not obscure other windows.
+     */
+    NOT_VISIBLE                  = 1 << 1,
+
+    /**
+     * Indicates that this input window cannot be a focus target, and this will not
+     * receive any input events that can only be directed for the focused window, such
+     * as key events.
+     */
+    NOT_FOCUSABLE                = 1 << 2,
+
+    /**
+     * Indicates that this input window cannot receive any events directed at a
+     * specific location on the screen, such as touchscreen, mouse, and stylus events.
+     * The window will not be considered as a touch target, but can still obscure other
+     * windows.
+     */
+    NOT_TOUCHABLE                = 1 << 3,
+
+    /**
+     * Indicates that this window will not accept a touch event that is split between
+     * more than one window. When set:
+     *  - If this window receives a DOWN event with the first pointer, all successive
+     *    pointers that go down, regardless of their location on the screen, will be
+     *    directed to this window;
+     *  - If the DOWN event lands outside the touchable bounds of this window, no
+     *    successive pointers that go down, regardless of their location on the screen,
+     *    will be directed to this window.
+     */
+    PREVENT_SPLITTING            = 1 << 4,
+
+    /**
+     * Indicates that this window shows the wallpaper behind it, so all touch events
+     * that it receives should also be sent to the wallpaper.
+     */
+    DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 5,
+
+    /** Indicates that this the wallpaper's input window. */
+    IS_WALLPAPER                 = 1 << 6,
+
+    /**
+     * Indicates that input events should not be dispatched to this window. When set,
+     * input events directed towards this window will simply be dropped, and will not
+     * be dispatched to windows behind it.
+     */
+    PAUSE_DISPATCHING            = 1 << 7,
+
+    /**
+     * This flag is set when the window is of a trusted type that is allowed to silently
+     * overlay other windows for the purpose of implementing the secure views feature.
+     * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+     * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+     */
+    TRUSTED_OVERLAY              = 1 << 8,
+
+    /**
+     * Indicates that this window wants to listen for when there is a touch DOWN event
+     * that occurs outside its touchable bounds. When such an event occurs, this window
+     * will receive a MotionEvent with ACTION_OUTSIDE.
+     */
+    WATCH_OUTSIDE_TOUCH          = 1 << 9,
+
+    /**
+     * When set, this flag allows touches to leave the current window whenever the finger
+     * moves above another window. When this happens, the window that touch has just left
+     * (the current window) will receive ACTION_CANCEL, and the window that touch has entered
+     * will receive ACTION_DOWN, and the remainder of the touch gesture will only go to the
+     * new window. Without this flag, the entire gesture is sent to the current window, even
+     * if the touch leaves the window's bounds.
+     */
+    SLIPPERY                     = 1 << 10,
+
+    /**
+     * When this window has focus, does not call user activity for all input events so
+     * the application will have to do it itself.
+     */
+    DISABLE_USER_ACTIVITY        = 1 << 11,
+
+    /**
+     * Internal flag used to indicate that input should be dropped on this window.
+     */
+    DROP_INPUT                   = 1 << 12,
+
+    /**
+     * Internal flag used to indicate that input should be dropped on this window if this window
+     * is obscured.
+     */
+    DROP_INPUT_IF_OBSCURED       = 1 << 13,
+
+    /**
+     * An input spy window. This window will receive all pointer events within its touchable
+     * area, but will not stop events from being sent to other windows below it in z-order.
+     * An input event will be dispatched to all spy windows above the top non-spy window at the
+     * event's coordinates.
+     */
+    SPY                          = 1 << 14,
+
+    /**
+     * When used with {@link #NOT_TOUCHABLE}, this window will continue to receive events from
+     * a stylus device within its touchable region. All other pointer events, such as from a
+     * mouse or touchscreen, will be dispatched to the windows behind it.
+     *
+     * This configuration has no effect when the config {@link #NOT_TOUCHABLE} is not set.
+     *
+     * It is not valid to set this configuration if {@link #TRUSTED_OVERLAY} is not set.
+     */
+    INTERCEPTS_STYLUS            = 1 << 15,
+}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index a3eebdd..8240b08 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -111,6 +111,7 @@
 struct {
     std::mutex lock;
     std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+    std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
     bool registeredToDisplayManager GUARDED_BY(lock) = false;
 
     std::atomic<nsecs_t> mLastKnownVsync = -1;
@@ -160,6 +161,7 @@
     void scheduleCallbacks();
 
     ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+    void registerStartTime() const;
 
     std::mutex mLock;
     // Protected by mLock
@@ -172,6 +174,9 @@
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
+
+    // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
+    static constexpr size_t kMaxStartTimes = 250;
 };
 
 static thread_local Choreographer* gChoreographer;
@@ -392,6 +397,7 @@
         if (cb.vsyncCallback != nullptr) {
             const ChoreographerFrameCallbackDataImpl frameCallbackData =
                     createFrameCallbackData(timestamp);
+            registerStartTime();
             mInCallback = true;
             cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
                                      &frameCallbackData),
@@ -452,6 +458,16 @@
             .choreographer = this};
 }
 
+void Choreographer::registerStartTime() const {
+    std::scoped_lock _l(gChoreographers.lock);
+    for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
+        while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
+            gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
+        }
+        gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+}
+
 } // namespace android
 using namespace android;
 
@@ -566,6 +582,16 @@
     return AChoreographer_to_Choreographer(choreographer)->getFrameInterval();
 }
 
+int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
+    std::scoped_lock _l(gChoreographers.lock);
+    const auto iter = gChoreographers.startTimes.find(vsyncId);
+    if (iter == gChoreographers.startTimes.end()) {
+        ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
+        return 0;
+    }
+    return iter->second;
+}
+
 } // namespace android
 
 /* Glue for the NDK interface */
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 19be5bd..cdf7e66 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -71,4 +71,9 @@
 int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index);
 
+// Gets the start time (dispatch time) in nanos of the callback, given a vsync ID. This does not
+// account for clients that use multiple choreographers, because callbacks that give the same vsync
+// ID may be dispatched at different times.
+int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId);
+
 } // namespace android
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 2da10cd..969d937 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -42,6 +42,7 @@
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos*;
+      android::AChoreographer_getStartTimeNanosForVsyncId*;
       android::AChoreographer_signalRefreshRateCallbacks*;
       android::AChoreographer_getFrameInterval*;
       android::ADisplay_acquirePhysicalDisplays*;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 07c5dd8..84e84dd 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -122,6 +122,9 @@
         ":librenderengine_threaded_sources",
         ":librenderengine_skia_sources",
     ],
+    header_libs: [
+        "libtonemap_headers",
+    ],
     include_dirs: [
         "external/skia/src/gpu",
     ],
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index a426850..d91af1e 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -42,6 +42,9 @@
         "libshaders",
         "libtonemap",
     ],
+    header_libs: [
+        "libtonemap_headers",
+    ],
 
     shared_libs: [
         "libbase",
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 390b228..1cd143e 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -38,6 +38,10 @@
         "libui-types",
     ],
 
+    header_libs: [
+        "libtonemap_headers",
+    ],
+
     srcs: [
         "shaders.cpp",
     ],
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 5360fe2..99d1b22 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -25,7 +25,6 @@
     name: "libtonemap",
     vendor_available: true,
 
-    export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 
     shared_libs: [
@@ -41,3 +40,9 @@
         "tonemap.cpp",
     ],
 }
+
+cc_library_headers {
+    name: "libtonemap_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index f46f3fa..d519482 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -27,6 +27,9 @@
     srcs: [
         "tonemap_test.cpp",
     ],
+    header_libs: [
+        "libtonemap_headers",
+    ],
     shared_libs: [
         "android.hardware.graphics.common-V3-ndk",
     ],
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index b34d906..42dd85e 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -134,6 +134,10 @@
     return mMatrix[1][1];
 }
 
+float Transform::det() const {
+    return mMatrix[0][0] * mMatrix[1][1] - mMatrix[0][1] * mMatrix[1][0];
+}
+
 float Transform::getScaleX() const {
     return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
 }
@@ -390,7 +394,7 @@
         const float x = M[2][0];
         const float y = M[2][1];
 
-        const float idet = 1.0f / (a*d - b*c);
+        const float idet = 1.0f / det();
         result.mMatrix[0][0] =  d*idet;
         result.mMatrix[0][1] = -c*idet;
         result.mMatrix[1][0] = -b*idet;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 33fbe05..f1178ca 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -77,6 +77,7 @@
     float dtdx() const;
     float dtdy() const;
     float dsdy() const;
+    float det() const;
 
     float getScaleX() const;
     float getScaleY() const;
diff --git a/opengl/OWNERS b/opengl/OWNERS
index a9bd4bb..a47fb9a 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,6 +1,12 @@
+abdolrashidi@google.com
+cclao@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 ianelliott@google.com
 jessehall@google.com
+lfy@google.com
 lpy@google.com
 timvp@google.com
+romanl@google.com
+vantablack@google.com
+yuxinhu@google.com
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 67ce9f2..974ae38 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -39,17 +39,30 @@
 #include <map>
 #include <mutex>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "gpuwork/gpu_work.h"
 
-#define MS_IN_NS (1000000)
+#define ONE_MS_IN_NS (10000000)
 
 namespace android {
 namespace gpuwork {
 
 namespace {
 
+bool lessThanGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) {
+    return std::tie(l.gpu_id, l.uid) < std::tie(r.gpu_id, r.uid);
+}
+
+size_t hashGpuIdUid(const android::gpuwork::GpuIdUid& gpuIdUid) {
+    return static_cast<size_t>((gpuIdUid.gpu_id << 5U) + gpuIdUid.uid);
+}
+
+bool equalGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) {
+    return std::tie(l.gpu_id, l.uid) == std::tie(r.gpu_id, r.uid);
+}
+
 // Gets a BPF map from |mapPath|.
 template <class Key, class Value>
 bool getBpfMap(const char* mapPath, bpf::BpfMap<Key, Value>* out) {
@@ -76,24 +89,6 @@
     return result;
 }
 
-template <>
-inline int32_t cast_int32<uint64_t>(uint64_t source) {
-    if (source > std::numeric_limits<int32_t>::max()) {
-        return std::numeric_limits<int32_t>::max();
-    }
-    return static_cast<int32_t>(source);
-}
-
-template <>
-inline int32_t cast_int32<long long>(long long source) {
-    if (source > std::numeric_limits<int32_t>::max()) {
-        return std::numeric_limits<int32_t>::max();
-    } else if (source < std::numeric_limits<int32_t>::min()) {
-        return std::numeric_limits<int32_t>::min();
-    }
-    return static_cast<int32_t>(source);
-}
-
 } // namespace
 
 using base::StringAppendF;
@@ -115,7 +110,7 @@
     {
         std::scoped_lock<std::mutex> lock(mMutex);
         if (mStatsdRegistered) {
-            AStatsManager_clearPullAtomCallback(android::util::GPU_FREQ_TIME_IN_STATE_PER_UID);
+            AStatsManager_clearPullAtomCallback(android::util::GPU_WORK_PER_UID);
         }
     }
 
@@ -144,7 +139,7 @@
         mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
     }
 
-    // Attach the tracepoint ONLY if we got the map above.
+    // Attach the tracepoint.
     if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power",
                           "gpu_work_period")) {
         return;
@@ -157,8 +152,8 @@
 
     {
         std::lock_guard<std::mutex> lock(mMutex);
-        AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
-                                          nullptr, GpuWork::pullAtomCallback, this);
+        AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_WORK_PER_UID}, nullptr,
+                                          GpuWork::pullAtomCallback, this);
         mStatsdRegistered = true;
     }
 
@@ -169,18 +164,18 @@
 
 void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) {
     if (!mInitialized.load()) {
-        result->append("GPU time in state information is not available.\n");
+        result->append("GPU work information is not available.\n");
         return;
     }
 
-    // Ordered map ensures output data is sorted by UID.
-    std::map<Uid, UidTrackingInfo> dumpMap;
+    // Ordered map ensures output data is sorted.
+    std::map<GpuIdUid, UidTrackingInfo, decltype(lessThanGpuIdUid)*> dumpMap(&lessThanGpuIdUid);
 
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
         if (!mGpuWorkMap.isValid()) {
-            result->append("GPU time in state map is not available.\n");
+            result->append("GPU work map is not available.\n");
             return;
         }
 
@@ -189,56 +184,42 @@
         // threads. The buckets are all preallocated. Our eBPF program only updates
         // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
         // map while holding |mMutex|. Given this, we should be able to iterate over
-        // all elements reliably. In the worst case, we might see elements more than
-        // once.
+        // all elements reliably. Nevertheless, we copy into a map to avoid
+        // duplicates.
 
         // Note that userspace reads of BPF maps make a copy of the value, and
         // thus the returned value is not being concurrently accessed by the BPF
         // program (no atomic reads needed below).
 
-        mGpuWorkMap.iterateWithValue([&dumpMap](const Uid& key, const UidTrackingInfo& value,
-                                                const android::bpf::BpfMap<Uid, UidTrackingInfo>&)
-                                             -> base::Result<void> {
-            dumpMap[key] = value;
-            return {};
-        });
+        mGpuWorkMap.iterateWithValue(
+                [&dumpMap](const GpuIdUid& key, const UidTrackingInfo& value,
+                           const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&)
+                        -> base::Result<void> {
+                    dumpMap[key] = value;
+                    return {};
+                });
     }
 
-    // Find the largest frequency where some UID has spent time in that frequency.
-    size_t largestFrequencyWithTime = 0;
-    for (const auto& uidToUidInfo : dumpMap) {
-        for (size_t i = largestFrequencyWithTime + 1; i < kNumTrackedFrequencies; ++i) {
-            if (uidToUidInfo.second.frequency_times_ns[i] > 0) {
-                largestFrequencyWithTime = i;
-            }
-        }
-    }
-
-    // Dump time in state information.
+    // Dump work information.
     // E.g.
-    // uid/freq: 0MHz 50MHz 100MHz ...
-    // 1000: 0 0 0 0 ...
-    // 1003: 0 0 3456 0 ...
-    // [errors:3]1006: 0 0 3456 0 ...
+    // GPU work information.
+    // gpu_id uid total_active_duration_ns total_inactive_duration_ns
+    // 0 1000 0 0
+    // 0 1003 1234 123
+    // [errors:3]0 1006 4567 456
 
     // Header.
-    result->append("GPU time in frequency state in ms.\n");
-    result->append("uid/freq: 0MHz");
-    for (size_t i = 1; i <= largestFrequencyWithTime; ++i) {
-        StringAppendF(result, " %zuMHz", i * 50);
-    }
-    result->append("\n");
+    result->append("GPU work information.\ngpu_id uid total_active_duration_ns "
+                   "total_inactive_duration_ns\n");
 
-    for (const auto& uidToUidInfo : dumpMap) {
-        if (uidToUidInfo.second.error_count) {
-            StringAppendF(result, "[errors:%" PRIu32 "]", uidToUidInfo.second.error_count);
+    for (const auto& idToUidInfo : dumpMap) {
+        if (idToUidInfo.second.error_count) {
+            StringAppendF(result, "[errors:%" PRIu32 "]", idToUidInfo.second.error_count);
         }
-        StringAppendF(result, "%" PRIu32 ":", uidToUidInfo.first);
-        for (size_t i = 0; i <= largestFrequencyWithTime; ++i) {
-            StringAppendF(result, " %" PRIu64,
-                          uidToUidInfo.second.frequency_times_ns[i] / MS_IN_NS);
-        }
-        result->append("\n");
+        StringAppendF(result, "%" PRIu32 " %" PRIu32 " %" PRIu64 " %" PRIu64 "\n",
+                      idToUidInfo.first.gpu_id, idToUidInfo.first.uid,
+                      idToUidInfo.second.total_active_duration_ns,
+                      idToUidInfo.second.total_inactive_duration_ns);
     }
 }
 
@@ -275,14 +256,14 @@
     ATRACE_CALL();
 
     GpuWork* gpuWork = reinterpret_cast<GpuWork*>(cookie);
-    if (atomTag == android::util::GPU_FREQ_TIME_IN_STATE_PER_UID) {
-        return gpuWork->pullFrequencyAtoms(data);
+    if (atomTag == android::util::GPU_WORK_PER_UID) {
+        return gpuWork->pullWorkAtoms(data);
     }
 
     return AStatsManager_PULL_SKIP;
 }
 
-AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList* data) {
+AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* data) {
     ATRACE_CALL();
 
     if (!data || !mInitialized.load()) {
@@ -295,96 +276,153 @@
         return AStatsManager_PULL_SKIP;
     }
 
-    std::unordered_map<Uid, UidTrackingInfo> uidInfos;
+    std::unordered_map<GpuIdUid, UidTrackingInfo, decltype(hashGpuIdUid)*, decltype(equalGpuIdUid)*>
+            workMap(32, &hashGpuIdUid, &equalGpuIdUid);
 
     // Iteration of BPF hash maps can be unreliable (no data races, but elements
     // may be repeated), as the map is typically being modified by other
     // threads. The buckets are all preallocated. Our eBPF program only updates
     // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
     // map while holding |mMutex|. Given this, we should be able to iterate over
-    // all elements reliably. In the worst case, we might see elements more than
-    // once.
+    // all elements reliably. Nevertheless, we copy into a map to avoid
+    // duplicates.
 
     // Note that userspace reads of BPF maps make a copy of the value, and thus
     // the returned value is not being concurrently accessed by the BPF program
     // (no atomic reads needed below).
 
-    mGpuWorkMap.iterateWithValue(
-            [&uidInfos](const Uid& key, const UidTrackingInfo& value,
-                        const android::bpf::BpfMap<Uid, UidTrackingInfo>&) -> base::Result<void> {
-                uidInfos[key] = value;
-                return {};
-            });
-
-    ALOGI("pullFrequencyAtoms: uidInfos.size() == %zu", uidInfos.size());
+    mGpuWorkMap.iterateWithValue([&workMap](const GpuIdUid& key, const UidTrackingInfo& value,
+                                            const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&)
+                                         -> base::Result<void> {
+        workMap[key] = value;
+        return {};
+    });
 
     // Get a list of just the UIDs; the order does not matter.
     std::vector<Uid> uids;
-    for (const auto& pair : uidInfos) {
-        uids.push_back(pair.first);
+    // Get a list of the GPU IDs, in order.
+    std::set<uint32_t> gpuIds;
+    {
+        // To avoid adding duplicate UIDs.
+        std::unordered_set<Uid> addedUids;
+
+        for (const auto& workInfo : workMap) {
+            if (addedUids.insert(workInfo.first.uid).second) {
+                // Insertion was successful.
+                uids.push_back(workInfo.first.uid);
+            }
+            gpuIds.insert(workInfo.first.gpu_id);
+        }
     }
 
+    ALOGI("pullWorkAtoms: uids.size() == %zu", uids.size());
+    ALOGI("pullWorkAtoms: gpuIds.size() == %zu", gpuIds.size());
+
+    if (gpuIds.size() > kNumGpusHardLimit) {
+        // If we observe a very high number of GPUs then something has probably
+        // gone wrong, so don't log any atoms.
+        return AStatsManager_PULL_SKIP;
+    }
+
+    size_t numSampledUids = kNumSampledUids;
+
+    if (gpuIds.size() > kNumGpusSoftLimit) {
+        // If we observe a high number of GPUs then we just sample 1 UID.
+        numSampledUids = 1;
+    }
+
+    // Remove all UIDs that do not have at least |kMinGpuTimeNanoseconds| on at
+    // least one GPU.
+    {
+        auto uidIt = uids.begin();
+        while (uidIt != uids.end()) {
+            bool hasEnoughGpuTime = false;
+            for (uint32_t gpuId : gpuIds) {
+                auto infoIt = workMap.find(GpuIdUid{gpuId, *uidIt});
+                if (infoIt == workMap.end()) {
+                    continue;
+                }
+                if (infoIt->second.total_active_duration_ns +
+                            infoIt->second.total_inactive_duration_ns >=
+                    kMinGpuTimeNanoseconds) {
+                    hasEnoughGpuTime = true;
+                    break;
+                }
+            }
+            if (hasEnoughGpuTime) {
+                ++uidIt;
+            } else {
+                uidIt = uids.erase(uidIt);
+            }
+        }
+    }
+
+    ALOGI("pullWorkAtoms: after removing uids with very low GPU time: uids.size() == %zu",
+          uids.size());
+
     std::random_device device;
     std::default_random_engine random_engine(device());
 
-    // If we have more than |kNumSampledUids| UIDs, choose |kNumSampledUids|
+    // If we have more than |numSampledUids| UIDs, choose |numSampledUids|
     // random UIDs. We swap them to the front of the list. Given the list
     // indices 0..i..n-1, we have the following inclusive-inclusive ranges:
     // - [0, i-1] == the randomly chosen elements.
     // - [i, n-1] == the remaining unchosen elements.
-    if (uids.size() > kNumSampledUids) {
-        for (size_t i = 0; i < kNumSampledUids; ++i) {
+    if (uids.size() > numSampledUids) {
+        for (size_t i = 0; i < numSampledUids; ++i) {
             std::uniform_int_distribution<size_t> uniform_dist(i, uids.size() - 1);
             size_t random_index = uniform_dist(random_engine);
             std::swap(uids[i], uids[random_index]);
         }
-        // Only keep the front |kNumSampledUids| elements.
-        uids.resize(kNumSampledUids);
+        // Only keep the front |numSampledUids| elements.
+        uids.resize(numSampledUids);
     }
 
-    ALOGI("pullFrequencyAtoms: uids.size() == %zu", uids.size());
+    ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size());
 
     auto now = std::chrono::steady_clock::now();
-
-    int32_t duration = cast_int32(
+    long long duration =
             std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
-                    .count());
+                    .count();
+    if (duration > std::numeric_limits<int32_t>::max() || duration < 0) {
+        // This is essentially impossible. If it does somehow happen, give up,
+        // but still clear the map.
+        clearMap();
+        return AStatsManager_PULL_SKIP;
+    }
 
-    for (const Uid uid : uids) {
-        const UidTrackingInfo& info = uidInfos[uid];
-        ALOGI("pullFrequencyAtoms: adding stats for UID %" PRIu32, uid);
-        android::util::addAStatsEvent(data, int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
-                                      // uid
-                                      bitcast_int32(uid),
-                                      // time_duration_seconds
-                                      int32_t{duration},
-                                      // max_freq_mhz
-                                      int32_t{1000},
-                                      // freq_0_mhz_time_millis
-                                      cast_int32(info.frequency_times_ns[0] / 1000000),
-                                      // freq_50_mhz_time_millis
-                                      cast_int32(info.frequency_times_ns[1] / 1000000),
-                                      // ... etc. ...
-                                      cast_int32(info.frequency_times_ns[2] / 1000000),
-                                      cast_int32(info.frequency_times_ns[3] / 1000000),
-                                      cast_int32(info.frequency_times_ns[4] / 1000000),
-                                      cast_int32(info.frequency_times_ns[5] / 1000000),
-                                      cast_int32(info.frequency_times_ns[6] / 1000000),
-                                      cast_int32(info.frequency_times_ns[7] / 1000000),
-                                      cast_int32(info.frequency_times_ns[8] / 1000000),
-                                      cast_int32(info.frequency_times_ns[9] / 1000000),
-                                      cast_int32(info.frequency_times_ns[10] / 1000000),
-                                      cast_int32(info.frequency_times_ns[11] / 1000000),
-                                      cast_int32(info.frequency_times_ns[12] / 1000000),
-                                      cast_int32(info.frequency_times_ns[13] / 1000000),
-                                      cast_int32(info.frequency_times_ns[14] / 1000000),
-                                      cast_int32(info.frequency_times_ns[15] / 1000000),
-                                      cast_int32(info.frequency_times_ns[16] / 1000000),
-                                      cast_int32(info.frequency_times_ns[17] / 1000000),
-                                      cast_int32(info.frequency_times_ns[18] / 1000000),
-                                      cast_int32(info.frequency_times_ns[19] / 1000000),
-                                      // freq_1000_mhz_time_millis
-                                      cast_int32(info.frequency_times_ns[20] / 1000000));
+    // Log an atom for each (gpu id, uid) pair for which we have data.
+    for (uint32_t gpuId : gpuIds) {
+        for (Uid uid : uids) {
+            auto it = workMap.find(GpuIdUid{gpuId, uid});
+            if (it == workMap.end()) {
+                continue;
+            }
+            const UidTrackingInfo& info = it->second;
+
+            uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS;
+            uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS;
+
+            // Skip this atom if any numbers are out of range. |duration| is
+            // already checked above.
+            if (total_active_duration_ms > std::numeric_limits<int32_t>::max() ||
+                total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) {
+                continue;
+            }
+
+            ALOGI("pullWorkAtoms: adding stats for GPU ID %" PRIu32 "; UID %" PRIu32, gpuId, uid);
+            android::util::addAStatsEvent(data, int32_t{android::util::GPU_WORK_PER_UID},
+                                          // uid
+                                          bitcast_int32(uid),
+                                          // gpu_id
+                                          bitcast_int32(gpuId),
+                                          // time_duration_seconds
+                                          static_cast<int32_t>(duration),
+                                          // total_active_duration_millis
+                                          static_cast<int32_t>(total_active_duration_ms),
+                                          // total_inactive_duration_millis
+                                          static_cast<int32_t>(total_inactive_duration_ms));
+        }
     }
     clearMap();
     return AStatsManager_PULL_SUCCESS;
@@ -435,7 +473,7 @@
     uint64_t numEntries = globalData.value().num_map_entries;
 
     // If the map is <=75% full, we do nothing.
-    if (numEntries <= (kMaxTrackedUids / 4) * 3) {
+    if (numEntries <= (kMaxTrackedGpuIdUids / 4) * 3) {
         return;
     }
 
@@ -456,22 +494,22 @@
 
     // Iterating BPF maps to delete keys is tricky. If we just repeatedly call
     // |getFirstKey()| and delete that, we may loop forever (or for a long time)
-    // because our BPF program might be repeatedly re-adding UID keys. Also,
-    // even if we limit the number of elements we try to delete, we might only
-    // delete new entries, leaving old entries in the map. If we delete a key A
-    // and then call |getNextKey(A)|, the first key in the map is returned, so
-    // we have the same issue.
+    // because our BPF program might be repeatedly re-adding keys. Also, even if
+    // we limit the number of elements we try to delete, we might only delete
+    // new entries, leaving old entries in the map. If we delete a key A and
+    // then call |getNextKey(A)|, the first key in the map is returned, so we
+    // have the same issue.
     //
     // Thus, we instead get the next key and then delete the previous key. We
     // also limit the number of deletions we try, just in case.
 
-    base::Result<Uid> key = mGpuWorkMap.getFirstKey();
+    base::Result<GpuIdUid> key = mGpuWorkMap.getFirstKey();
 
-    for (size_t i = 0; i < kMaxTrackedUids; ++i) {
+    for (size_t i = 0; i < kMaxTrackedGpuIdUids; ++i) {
         if (!key.ok()) {
             break;
         }
-        base::Result<Uid> previousKey = key;
+        base::Result<GpuIdUid> previousKey = key;
         key = mGpuWorkMap.getNextKey(previousKey.value());
         mGpuWorkMap.deleteValue(previousKey.value());
     }
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
index a0e1b22..c8e79a2 100644
--- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
+++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
@@ -27,66 +27,124 @@
 #endif
 
 #define S_IN_NS (1000000000)
-#define MHZ_IN_KHZS (1000)
+#define SMALL_TIME_GAP_LIMIT_NS (S_IN_NS)
 
-typedef uint32_t Uid;
-
-// A map from UID to |UidTrackingInfo|.
-DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, Uid, UidTrackingInfo, kMaxTrackedUids, AID_GRAPHICS);
+// A map from GpuIdUid (GPU ID and application UID) to |UidTrackingInfo|.
+DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, GpuIdUid, UidTrackingInfo, kMaxTrackedGpuIdUids,
+                   AID_GRAPHICS);
 
 // A map containing a single entry of |GlobalData|.
 DEFINE_BPF_MAP_GRW(gpu_work_global_data, ARRAY, uint32_t, GlobalData, 1, AID_GRAPHICS);
 
-// GpuUidWorkPeriodEvent defines the structure of a kernel tracepoint under the
-// tracepoint system (also referred to as the group) "power" and name
-// "gpu_work_period". In summary, the kernel tracepoint should be
-// "power/gpu_work_period", available at:
+// Defines the structure of the kernel tracepoint:
 //
 //  /sys/kernel/tracing/events/power/gpu_work_period/
 //
-// GpuUidWorkPeriodEvent defines a non-overlapping, non-zero period of time when
-// work was running on the GPU for a given application (identified by its UID;
-// the persistent, unique ID of the application) from |start_time_ns| to
-// |end_time_ns|. Drivers should issue this tracepoint as soon as possible
-// (within 1 second) after |end_time_ns|. For a given UID, periods must not
-// overlap, but periods from different UIDs can overlap (and should overlap, if
-// and only if that is how the work was executed). The period includes
-// information such as |frequency_khz|, the frequency that the GPU was running
-// at during the period, and |includes_compute_work|, whether the work included
-// compute shader work (not just graphics work). GPUs may have multiple
-// frequencies that can be adjusted, but the driver should report the frequency
-// that most closely estimates power usage (e.g. the frequency of shader cores,
-// not a scheduling unit).
+// Drivers must define an appropriate gpu_work_period kernel tracepoint (for
+// example, using the DECLARE_EVENT_CLASS and DEFINE_EVENT macros) such that the
+// arguments/fields match the fields of |GpuWorkPeriodEvent|, excluding the
+// initial "common" field. Drivers must invoke the tracepoint (also referred to
+// as emitting the event) as described below. Note that the description below
+// assumes a single physical GPU and its driver; for devices with multiple GPUs,
+// each GPU and its driver should emit events independently, using a different
+// value for |gpu_id| per GPU.
 //
-// If any information changes while work from the UID is running on the GPU
-// (e.g. the GPU frequency changes, or the work starts/stops including compute
-// work) then the driver must conceptually end the period, issue the tracepoint,
-// start tracking a new period, and eventually issue a second tracepoint when
-// the work completes or when the information changes again. In this case, the
-// |end_time_ns| of the first period must equal the |start_time_ns| of the
-// second period. The driver may also end and start a new period (without a
-// gap), even if no information changes. For example, this might be convenient
-// if there is a collection of work from a UID running on the GPU for a long
-// time; ending and starting a period as individual parts of the work complete
-// allows the consumer of the tracepoint to be updated about the ongoing work.
+// |GpuWorkPeriodEvent| defines a non-overlapping, non-zero period of time from
+// |start_time_ns| (inclusive) until |end_time_ns| (exclusive) for a given
+// |uid|, and includes details of how much work the GPU was performing for |uid|
+// during the period. When GPU work for a given |uid| runs on the GPU, the
+// driver must track one or more periods that cover the time where the work was
+// running, and emit events soon after. The driver should try to emit the event
+// for a period at most 1 second after |end_time_ns|, and must emit the event at
+// most 2 seconds after |end_time_ns|. A period's duration (|end_time_ns| -
+// |start_time_ns|) must be at most 1 second. Periods for different |uids| can
+// overlap, but periods for the same |uid| must not overlap. The driver must
+// emit events for the same |uid| in strictly increasing order of
+// |start_time_ns|, such that it is guaranteed that the tracepoint call for a
+// period for |uid| has returned before the tracepoint call for the next period
+// for |uid| is made. Note that synchronization may be necessary if the driver
+// emits events for the same |uid| from different threads/contexts. Note that
+// |end_time_ns| for a period for a |uid| may equal the |start_time_ns| of the
+// next period for |uid|. The driver should try to avoid emitting a large number
+// of events in a short time period (e.g. 1000 events per second) for a given
+// |uid|.
 //
-// For a given UID, the tracepoints must be emitted in order; that is, the
-// |start_time_ns| of each subsequent tracepoint for a given UID must be
-// monotonically increasing.
+// The |total_active_duration_ns| must be set to the approximate total amount of
+// time the GPU spent running work for |uid| within the period, without
+// "double-counting" parallel GPU work on the same GPU for the same |uid|. "GPU
+// work" should correspond to the "GPU slices" shown in the AGI (Android GPU
+// Inspector) tool, and so should include work such as fragment and non-fragment
+// work/shaders running on the shader cores of the GPU. For example, given the
+// following:
+//  - A period has:
+//    - |start_time_ns|: 100,000,000 ns
+//    - |end_time_ns|:   800,000,000 ns
+//  - Some GPU vertex work (A):
+//    - started at:      200,000,000 ns
+//    - ended at:        400,000,000 ns
+//  - Some GPU fragment work (B):
+//    - started at:      300,000,000 ns
+//    - ended at:        500,000,000 ns
+//  - Some GPU fragment work (C):
+//    - started at:      300,000,000 ns
+//    - ended at:        400,000,000 ns
+//  - Some GPU fragment work (D):
+//    - started at:      600,000,000 ns
+//    - ended at:        700,000,000 ns
+//
+// The |total_active_duration_ns| would be 400,000,000 ns, because GPU work for
+// |uid| was executing:
+//  - from 200,000,000 ns to 500,000,000 ns, giving a duration of 300,000,000 ns
+//    (encompassing GPU work A, B, and C)
+//  - from 600,000,000 ns to 700,000,000 ns, giving a duration of 100,000,000 ns
+//    (GPU work D)
+//
+// Thus, the |total_active_duration_ns| is the sum of the (non-overlapping)
+// durations. Drivers may not have efficient access to the exact start and end
+// times of all GPU work, as shown above, but drivers should try to
+// approximate/aggregate the value of |total_active_duration_ns| as accurately
+// as possible within the limitations of the hardware, without double-counting
+// parallel GPU work for the same |uid|. The |total_active_duration_ns| value
+// must be less than or equal to the period duration (|end_time_ns| -
+// |start_time_ns|); if the aggregation approach might violate this requirement
+// then the driver must clamp |total_active_duration_ns| to be at most the
+// period duration.
+//
+// Note that the above description allows for a certain amount of flexibility in
+// how the driver tracks periods and emits the events. We list a few examples of
+// how drivers might implement the above:
+//
+// - 1: The driver could track periods for all |uid| values at fixed intervals
+//   of 1 second. Thus, every period duration would be exactly 1 second, and
+//   periods from different |uid|s that overlap would have the same
+//   |start_time_ns| and |end_time_ns| values.
+//
+// - 2: The driver could track periods with many different durations (up to 1
+//   second), as needed in order to cover the GPU work for each |uid|.
+//   Overlapping periods for different |uid|s may have very different durations,
+//   as well as different |start_time_ns| and |end_time_ns| values.
+//
+// - 3: The driver could track fine-grained periods with different durations
+//   that precisely cover the time where GPU work is running for each |uid|.
+//   Thus, |total_active_duration_ns| would always equal the period duration.
+//   For example, if a game was running at 60 frames per second, the driver
+//   would most likely emit _at least_ 60 events per second (probably more, as
+//   there would likely be multiple "chunks" of GPU work per frame, with gaps
+//   between each chunk). However, the driver may sometimes need to resort to
+//   more coarse-grained periods to avoid emitting thousands of events per
+//   second for a |uid|, where |total_active_duration_ns| would then be less
+//   than the period duration.
 typedef struct {
     // Actual fields start at offset 8.
-    uint64_t reserved;
+    uint64_t common;
 
-    // The UID of the process (i.e. persistent, unique ID of the Android app)
-    // that submitted work to the GPU.
+    // A value that uniquely identifies the GPU within the system.
+    uint32_t gpu_id;
+
+    // The UID of the application (i.e. persistent, unique ID of the Android
+    // app) that submitted work to the GPU.
     uint32_t uid;
 
-    // The GPU frequency during the period. GPUs may have multiple frequencies
-    // that can be adjusted, but the driver should report the frequency that
-    // would most closely estimate power usage (e.g. the frequency of shader
-    // cores, not a scheduling unit).
-    uint32_t frequency_khz;
-
     // The start time of the period in nanoseconds. The clock must be
     // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
     uint64_t start_time_ns;
@@ -95,54 +153,44 @@
     // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
     uint64_t end_time_ns;
 
-    // Flags about the work. Reserved for future use.
-    uint64_t flags;
+    // The amount of time the GPU was running GPU work for |uid| during the
+    // period, in nanoseconds, without double-counting parallel GPU work for the
+    // same |uid|. For example, this might include the amount of time the GPU
+    // spent performing shader work (vertex work, fragment work, etc.) for
+    // |uid|.
+    uint64_t total_active_duration_ns;
 
-    // The maximum GPU frequency allowed during the period according to the
-    // thermal throttling policy. Must be 0 if no thermal throttling was
-    // enforced during the period. The value must relate to |frequency_khz|; in
-    // other words, if |frequency_khz| is the frequency of the GPU shader cores,
-    // then |thermally_throttled_max_frequency_khz| must be the maximum allowed
-    // frequency of the GPU shader cores (not the maximum allowed frequency of
-    // some other part of the GPU).
-    //
-    // Note: Unlike with other fields of this struct, if the
-    // |thermally_throttled_max_frequency_khz| value conceptually changes while
-    // work from a UID is running on the GPU then the GPU driver does NOT need
-    // to accurately report this by ending the period and starting to track a
-    // new period; instead, the GPU driver may report any one of the
-    // |thermally_throttled_max_frequency_khz| values that was enforced during
-    // the period. The motivation for this relaxation is that we assume the
-    // maximum frequency will not be changing rapidly, and so capturing the
-    // exact point at which the change occurs is unnecessary.
-    uint32_t thermally_throttled_max_frequency_khz;
+} GpuWorkPeriodEvent;
 
-} GpuUidWorkPeriodEvent;
-
-_Static_assert(offsetof(GpuUidWorkPeriodEvent, uid) == 8 &&
-                       offsetof(GpuUidWorkPeriodEvent, frequency_khz) == 12 &&
-                       offsetof(GpuUidWorkPeriodEvent, start_time_ns) == 16 &&
-                       offsetof(GpuUidWorkPeriodEvent, end_time_ns) == 24 &&
-                       offsetof(GpuUidWorkPeriodEvent, flags) == 32 &&
-                       offsetof(GpuUidWorkPeriodEvent, thermally_throttled_max_frequency_khz) == 40,
-               "Field offsets of struct GpuUidWorkPeriodEvent must not be changed because they "
+_Static_assert(offsetof(GpuWorkPeriodEvent, gpu_id) == 8 &&
+                       offsetof(GpuWorkPeriodEvent, uid) == 12 &&
+                       offsetof(GpuWorkPeriodEvent, start_time_ns) == 16 &&
+                       offsetof(GpuWorkPeriodEvent, end_time_ns) == 24 &&
+                       offsetof(GpuWorkPeriodEvent, total_active_duration_ns) == 32,
+               "Field offsets of struct GpuWorkPeriodEvent must not be changed because they "
                "must match the tracepoint field offsets found via adb shell cat "
                "/sys/kernel/tracing/events/power/gpu_work_period/format");
 
 DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_gpu_work_period)
-(GpuUidWorkPeriodEvent* const args) {
-    // Note: In BPF programs, |__sync_fetch_and_add| is translated to an atomic
+(GpuWorkPeriodEvent* const period) {
+    // Note: In eBPF programs, |__sync_fetch_and_add| is translated to an atomic
     // add.
 
-    const uint32_t uid = args->uid;
+    // Return 1 to avoid blocking simpleperf from receiving events.
+    const int ALLOW = 1;
 
-    // Get |UidTrackingInfo| for |uid|.
-    UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+    GpuIdUid gpu_id_and_uid;
+    __builtin_memset(&gpu_id_and_uid, 0, sizeof(gpu_id_and_uid));
+    gpu_id_and_uid.gpu_id = period->gpu_id;
+    gpu_id_and_uid.uid = period->uid;
+
+    // Get |UidTrackingInfo|.
+    UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid);
     if (!uid_tracking_info) {
         // There was no existing entry, so we add a new one.
         UidTrackingInfo initial_info;
         __builtin_memset(&initial_info, 0, sizeof(initial_info));
-        if (0 == bpf_gpu_work_map_update_elem(&uid, &initial_info, BPF_NOEXIST)) {
+        if (0 == bpf_gpu_work_map_update_elem(&gpu_id_and_uid, &initial_info, BPF_NOEXIST)) {
             // We added an entry to the map, so we increment our entry counter in
             // |GlobalData|.
             const uint32_t zero = 0;
@@ -154,66 +202,80 @@
                 __sync_fetch_and_add(&global_data->num_map_entries, 1);
             }
         }
-        uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+        uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid);
         if (!uid_tracking_info) {
             // This should never happen, unless entries are getting deleted at
             // this moment. If so, we just give up.
-            return 0;
+            return ALLOW;
         }
     }
 
-    // The period duration must be non-zero.
-    if (args->start_time_ns >= args->end_time_ns) {
+    if (
+            // The period duration must be non-zero.
+            period->start_time_ns >= period->end_time_ns ||
+            // The period duration must be at most 1 second.
+            (period->end_time_ns - period->start_time_ns) > S_IN_NS) {
         __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
-        return 0;
+        return ALLOW;
     }
 
-    // The frequency must not be 0.
-    if (args->frequency_khz == 0) {
-        __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
-        return 0;
+    // If |total_active_duration_ns| is 0 then no GPU work occurred and there is
+    // nothing to do.
+    if (period->total_active_duration_ns == 0) {
+        return ALLOW;
     }
 
-    // Calculate the frequency index: see |UidTrackingInfo.frequency_times_ns|.
-    // Round to the nearest 50MHz bucket.
-    uint32_t frequency_index =
-            ((args->frequency_khz / MHZ_IN_KHZS) + (kFrequencyGranularityMhz / 2)) /
-            kFrequencyGranularityMhz;
-    if (frequency_index >= kNumTrackedFrequencies) {
-        frequency_index = kNumTrackedFrequencies - 1;
-    }
+    // Update |uid_tracking_info->total_active_duration_ns|.
+    __sync_fetch_and_add(&uid_tracking_info->total_active_duration_ns,
+                         period->total_active_duration_ns);
 
-    // Never round down to 0MHz, as this is a special bucket (see below) and not
-    // an actual operating point.
-    if (frequency_index == 0) {
-        frequency_index = 1;
-    }
-
-    // Update time in state.
-    __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index],
-                         args->end_time_ns - args->start_time_ns);
-
-    if (uid_tracking_info->previous_end_time_ns > args->start_time_ns) {
-        // This must not happen because per-UID periods must not overlap and
-        // must be emitted in order.
+    // |small_gap_time_ns| is the time gap between the current and previous
+    // active period, which could be 0. If the gap is more than
+    // |SMALL_TIME_GAP_LIMIT_NS| then |small_gap_time_ns| will be set to 0
+    // because we want to estimate the small gaps between "continuous" GPU work.
+    uint64_t small_gap_time_ns = 0;
+    if (uid_tracking_info->previous_active_end_time_ns > period->start_time_ns) {
+        // The current period appears to have occurred before the previous
+        // active period, which must not happen because per-UID periods must not
+        // overlap and must be emitted in strictly increasing order of
+        // |start_time_ns|.
         __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
     } else {
-        // The period appears to have been emitted after the previous, as
-        // expected, so we can calculate the gap between this and the previous
-        // period.
-        const uint64_t gap_time = args->start_time_ns - uid_tracking_info->previous_end_time_ns;
+        // The current period appears to have been emitted after the previous
+        // active period, as expected, so we can calculate the gap between the
+        // current and previous active period.
+        small_gap_time_ns = period->start_time_ns - uid_tracking_info->previous_active_end_time_ns;
 
-        // Update |previous_end_time_ns|.
-        uid_tracking_info->previous_end_time_ns = args->end_time_ns;
+        // Update |previous_active_end_time_ns|.
+        uid_tracking_info->previous_active_end_time_ns = period->end_time_ns;
 
-        // Update the special 0MHz frequency time, which stores the gaps between
-        // periods, but only if the gap is < 1 second.
-        if (gap_time > 0 && gap_time < S_IN_NS) {
-            __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[0], gap_time);
+        // We want to estimate the small gaps between "continuous" GPU work; if
+        // the gap is more than |SMALL_TIME_GAP_LIMIT_NS| then we don't consider
+        // this "continuous" GPU work.
+        if (small_gap_time_ns > SMALL_TIME_GAP_LIMIT_NS) {
+            small_gap_time_ns = 0;
         }
     }
 
-    return 0;
+    uint64_t period_total_inactive_time_ns = 0;
+    const uint64_t period_duration_ns = period->end_time_ns - period->start_time_ns;
+    // |period->total_active_duration_ns| is the active time within the period duration, so
+    // it must not be larger than |period_duration_ns|.
+    if (period->total_active_duration_ns > period_duration_ns) {
+        __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+    } else {
+        period_total_inactive_time_ns = period_duration_ns - period->total_active_duration_ns;
+    }
+
+    // Update |uid_tracking_info->total_inactive_duration_ns| by adding the
+    // inactive time from this period, plus the small gap between the current
+    // and previous active period. Either or both of these values could be 0.
+    if (small_gap_time_ns > 0 || period_total_inactive_time_ns > 0) {
+        __sync_fetch_and_add(&uid_tracking_info->total_inactive_duration_ns,
+                             small_gap_time_ns + period_total_inactive_time_ns);
+    }
+
+    return ALLOW;
 }
 
 LICENSE("Apache 2.0");
diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
index 4fe8464..57338f4 100644
--- a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
+++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
@@ -25,18 +25,28 @@
 namespace gpuwork {
 #endif
 
+typedef struct  {
+    uint32_t gpu_id;
+    uint32_t uid;
+} GpuIdUid;
+
 typedef struct {
-    // The end time of the previous period from this UID in nanoseconds.
-    uint64_t previous_end_time_ns;
+    // The end time of the previous period where the GPU was active for the UID,
+    // in nanoseconds.
+    uint64_t previous_active_end_time_ns;
 
-    // The time spent at each GPU frequency while running GPU work from the UID,
-    // in nanoseconds. Array index i stores the time for frequency i*50 MHz. So
-    // index 0 is 0Mhz, index 1 is 50MHz, index 2 is 100MHz, etc., up to index
-    // |kNumTrackedFrequencies|.
-    uint64_t frequency_times_ns[21];
+    // The total amount of time the GPU has spent running work for the UID, in
+    // nanoseconds.
+    uint64_t total_active_duration_ns;
 
-    // The number of times we received |GpuUidWorkPeriodEvent| events in an
-    // unexpected order. See |GpuUidWorkPeriodEvent|.
+    // The total amount of time of the "gaps" between "continuous" GPU work for
+    // the UID, in nanoseconds. This is estimated by ignoring large gaps between
+    // GPU work for this UID.
+    uint64_t total_inactive_duration_ns;
+
+    // The number of errors detected due to |GpuWorkPeriodEvent| events for the
+    // UID violating the specification in some way. E.g. periods with a zero or
+    // negative duration.
     uint32_t error_count;
 
 } UidTrackingInfo;
@@ -48,14 +58,10 @@
     uint64_t num_map_entries;
 } GlobalData;
 
-static const uint32_t kMaxTrackedUids = 512;
-static const uint32_t kFrequencyGranularityMhz = 50;
-static const uint32_t kNumTrackedFrequencies = 21;
+// The maximum number of tracked GPU ID and UID pairs (|GpuIdUid|).
+static const uint32_t kMaxTrackedGpuIdUids = 512;
 
 #ifdef __cplusplus
-static_assert(kNumTrackedFrequencies ==
-              std::extent<decltype(UidTrackingInfo::frequency_times_ns)>::value);
-
 } // namespace gpuwork
 } // namespace android
 #endif
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index b6f493d..acecd56 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -41,7 +41,7 @@
 
     void initialize();
 
-    // Dumps the GPU time in frequency state information.
+    // Dumps the GPU work information.
     void dump(const Vector<String16>& args, std::string* result);
 
 private:
@@ -55,7 +55,7 @@
                                                                  AStatsEventList* data,
                                                                  void* cookie);
 
-    AStatsManager_PullAtomCallbackReturn pullFrequencyAtoms(AStatsEventList* data);
+    AStatsManager_PullAtomCallbackReturn pullWorkAtoms(AStatsEventList* data);
 
     // Periodically calls |clearMapIfNeeded| to clear the |mGpuWorkMap| map, if
     // needed.
@@ -88,7 +88,7 @@
     std::mutex mMutex;
 
     // BPF map for per-UID GPU work.
-    bpf::BpfMap<Uid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
+    bpf::BpfMap<GpuIdUid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
 
     // BPF map containing a single element for global data.
     bpf::BpfMap<uint32_t, GlobalData> mGpuWorkGlobalDataMap GUARDED_BY(mMutex);
@@ -110,7 +110,18 @@
     bool mStatsdRegistered GUARDED_BY(mMutex) = false;
 
     // The number of randomly chosen (i.e. sampled) UIDs to log stats for.
-    static constexpr int kNumSampledUids = 10;
+    static constexpr size_t kNumSampledUids = 10;
+
+    // A "large" number of GPUs. If we observe more GPUs than this limit then
+    // we reduce the amount of stats we log.
+    static constexpr size_t kNumGpusSoftLimit = 4;
+
+    // A "very large" number of GPUs. If we observe more GPUs than this limit
+    // then we don't log any stats.
+    static constexpr size_t kNumGpusHardLimit = 32;
+
+    // The minimum GPU time needed to actually log stats for a UID.
+    static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds.
 
     // The previous time point at which |mGpuWorkMap| was cleared.
     std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
diff --git a/services/gpuservice/vts/AndroidTest.xml b/services/gpuservice/vts/AndroidTest.xml
index 02ca07f..34dc2ea 100644
--- a/services/gpuservice/vts/AndroidTest.xml
+++ b/services/gpuservice/vts/AndroidTest.xml
@@ -14,6 +14,9 @@
      limitations under the License.
 -->
 <configuration description="Runs GpuServiceVendorTests">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="force-root" value="false" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="jar" value="GpuServiceVendorTests.jar" />
     </test>
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 4a77d9a..9fa9016 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -78,12 +78,11 @@
                 "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;",
                 "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;",
                 "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;",
-                "\tfield:u32 uid;\toffset:8;\tsize:4;\tsigned:0;",
-                "\tfield:u32 frequency;\toffset:12;\tsize:4;\tsigned:0;",
-                "\tfield:u64 start_time;\toffset:16;\tsize:8;\tsigned:0;",
-                "\tfield:u64 end_time;\toffset:24;\tsize:8;\tsigned:0;",
-                "\tfield:u64 flags;\toffset:32;\tsize:8;\tsigned:0;",
-                "\tfield:u32 thermally_throttled_max_frequency_khz;\toffset:40;\tsize:4;\tsigned:0;"
+                "\tfield:u32 gpu_id;\toffset:8;\tsize:4;\tsigned:0;",
+                "\tfield:u32 uid;\toffset:12;\tsize:4;\tsigned:0;",
+                "\tfield:u64 start_time_ns;\toffset:16;\tsize:8;\tsigned:0;",
+                "\tfield:u64 end_time_ns;\toffset:24;\tsize:8;\tsigned:0;",
+                "\tfield:u64 total_active_duration_ns;\toffset:32;\tsize:8;\tsigned:0;"
         );
 
         // We use |fail| rather than |assertEquals| because it allows us to give a clearer message.
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index cd20a64..9691ad8 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -61,18 +61,13 @@
         ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
     }
 
-    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
                                   const std::string& reason) override {
         ALOGE("Window is not responding: %s", reason.c_str());
     }
 
-    void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {}
-
-    void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override {
-        ALOGE("Monitor is not responding: %s", reason.c_str());
-    }
-
-    void notifyMonitorResponsive(int32_t pid) override {}
+    void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+                                std::optional<int32_t> pid) override {}
 
     void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 58c9303..06ad6a8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -66,35 +66,77 @@
 
 namespace {
 
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+/**
+ * Log detailed debug messages about each inbound event notification to the dispatcher.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart)
+ */
+const bool DEBUG_INBOUND_EVENT_DETAILS =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
 
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+/**
+ * Log detailed debug messages about each outbound event processed by the dispatcher.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
+ */
+const bool DEBUG_OUTBOUND_EVENT_DETAILS =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);
 
-// Log debug messages about the dispatch cycle.
-constexpr bool DEBUG_DISPATCH_CYCLE = false;
+/**
+ * Log debug messages about the dispatch cycle.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherDispatchCycle DEBUG" (requires restart)
+ */
+const bool DEBUG_DISPATCH_CYCLE =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "DispatchCycle", ANDROID_LOG_INFO);
 
-// Log debug messages about channel creation
-constexpr bool DEBUG_CHANNEL_CREATION = false;
+/**
+ * Log debug messages about channel creation
+ * Enable this via "adb shell setprop log.tag.InputDispatcherChannelCreation DEBUG" (requires
+ * restart)
+ */
+const bool DEBUG_CHANNEL_CREATION =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "ChannelCreation", ANDROID_LOG_INFO);
 
-// Log debug messages about input event injection.
-constexpr bool DEBUG_INJECTION = false;
+/**
+ * Log debug messages about input event injection.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInjection DEBUG" (requires restart)
+ */
+const bool DEBUG_INJECTION =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Injection", ANDROID_LOG_INFO);
 
-// Log debug messages about input focus tracking.
-constexpr bool DEBUG_FOCUS = false;
+/**
+ * Log debug messages about input focus tracking.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
+ */
+const bool DEBUG_FOCUS =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Focus", ANDROID_LOG_INFO);
 
-// Log debug messages about touch mode event
-constexpr bool DEBUG_TOUCH_MODE = false;
+/**
+ * Log debug messages about touch mode event
+ * Enable this via "adb shell setprop log.tag.InputDispatcherTouchMode DEBUG" (requires restart)
+ */
+const bool DEBUG_TOUCH_MODE =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchMode", ANDROID_LOG_INFO);
 
-// Log debug messages about touch occlusion
-constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+/**
+ * Log debug messages about touch occlusion
+ * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
+ * restart)
+ */
+const bool DEBUG_TOUCH_OCCLUSION =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
 
-// Log debug messages about the app switch latency optimization.
-constexpr bool DEBUG_APP_SWITCH = false;
+/**
+ * Log debug messages about the app switch latency optimization.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart)
+ */
+const bool DEBUG_APP_SWITCH =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "AppSwitch", ANDROID_LOG_INFO);
 
-// Log debug messages about hover events.
-constexpr bool DEBUG_HOVER = false;
+/**
+ * Log debug messages about hover events.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart)
+ */
+const bool DEBUG_HOVER =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
 
 // Temporarily releases a held mutex for the lifetime of the instance.
 // Named to match std::scoped_lock
@@ -118,10 +160,7 @@
 // when an application takes too long to respond and the user has pressed an app switch key.
 constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
 
-// Amount of time to allow for an event to be dispatched (measured since its eventTime)
-// before considering it stale and dropping it.
-const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL // 10sec
-        * HwTimeoutMultiplier();
+const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier();
 
 // Log a warning when an event takes longer than this to process, even if an ANR does not occur.
 constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
@@ -322,10 +361,6 @@
             first->applicationInfo.token == second->applicationInfo.token;
 }
 
-bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
-    return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
-}
-
 std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
                                                    std::shared_ptr<EventEntry> eventEntry,
                                                    int32_t inputTargetFlags) {
@@ -526,6 +561,10 @@
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
+      : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+                                 std::chrono::nanoseconds staleEventTimeout)
       : mPolicy(policy),
         mPendingEvent(nullptr),
         mLastDropReason(DropReason::NOT_DROPPED),
@@ -544,6 +583,7 @@
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
+        mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator) {
     mLooper = new Looper(false);
@@ -901,6 +941,10 @@
     }
 }
 
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+    return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+}
+
 /**
  * Return true if the events preceding this incoming motion event should be dropped
  * Return false otherwise (the default behaviour)
@@ -2698,18 +2742,16 @@
 
 std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
                                                          bool isTouchedWindow) const {
-    return StringPrintf(INDENT2
-                        "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
-                        "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
-                        "], touchableRegion=%s, window={%s}, inputConfig={%s}, inputFeatures={%s}, "
-                        "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
+    return StringPrintf(INDENT2 "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+                                "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+                                "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
+                                "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
                         isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
                         info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(),
                         info->alpha, info->frameLeft, info->frameTop, info->frameRight,
                         info->frameBottom, dumpRegion(info->touchableRegion).c_str(),
                         info->name.c_str(), info->inputConfig.string().c_str(),
-                        info->inputFeatures.string().c_str(), toString(info->token != nullptr),
-                        info->applicationInfo.name.c_str(),
+                        toString(info->token != nullptr), info->applicationInfo.name.c_str(),
                         toString(info->applicationInfo.token).c_str());
 }
 
@@ -2787,7 +2829,7 @@
     sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     if (focusedWindowHandle != nullptr) {
         const WindowInfo* info = focusedWindowHandle->getInfo();
-        if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
+        if (info->inputConfig.test(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY)) {
             if (DEBUG_DISPATCH_CYCLE) {
                 ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
             }
@@ -4426,19 +4468,9 @@
     const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
 
     for (uint32_t i = 0; i < entry.pointerCount; i++) {
-        PointerCoords& pc = entry.pointerCoords[i];
-        // Make a copy of the injected coords. We cannot change them in place because some of them
-        // are interdependent (for example, X coordinate might depend on the Y coordinate).
-        PointerCoords injectedCoords = entry.pointerCoords[i];
-
-        BitSet64 bits(injectedCoords.bits);
-        while (!bits.isEmpty()) {
-            const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
-            const float value =
-                    MotionEvent::calculateTransformedAxisValue(axis, entry.source,
-                                                               transformToDisplay, injectedCoords);
-            pc.setAxisValue(axis, value);
-        }
+        entry.pointerCoords[i] =
+                MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
+                                                        entry.pointerCoords[i]);
     }
 }
 
@@ -4526,7 +4558,7 @@
 bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
     sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
     const bool noInputChannel =
-            windowHandle.getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+            windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
     if (connection != nullptr && noInputChannel) {
         ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
               windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
@@ -4576,7 +4608,7 @@
         const WindowInfo* info = handle->getInfo();
         if (getInputChannelLocked(handle->getToken()) == nullptr) {
             const bool noInputChannel =
-                    info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+                    info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
             const bool canReceiveInput =
                     !info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) ||
                     !info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE);
@@ -4642,7 +4674,7 @@
         const WindowInfo& info = *window->getInfo();
 
         // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
-        const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+        const bool noInputWindow = info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
         if (noInputWindow && window->getToken() != nullptr) {
             ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
                   window->getName().c_str());
@@ -5219,8 +5251,6 @@
                                          windowInfo->applicationInfo.name.c_str(),
                                          toString(windowInfo->applicationInfo.token).c_str());
                     dump += dumpRegion(windowInfo->touchableRegion);
-                    dump += StringPrintf(", inputFeatures=%s",
-                                         windowInfo->inputFeatures.string().c_str());
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
                                          "ms, hasToken=%s, "
                                          "touchOcclusionMode=%s\n",
@@ -5816,35 +5846,21 @@
     }
 }
 
-void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
-    auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
-        scoped_unlock unlock(mLock);
-        mPolicy->notifyMonitorUnresponsive(pid, reason);
-    };
-    postCommandLocked(std::move(command));
-}
-
 void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
+                                                          std::optional<int32_t> pid,
                                                           std::string reason) {
-    auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+    auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
         scoped_unlock unlock(mLock);
-        mPolicy->notifyWindowUnresponsive(token, reason);
+        mPolicy->notifyWindowUnresponsive(token, pid, reason);
     };
     postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
-    auto command = [this, pid]() REQUIRES(mLock) {
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token,
+                                                        std::optional<int32_t> pid) {
+    auto command = [this, token, pid]() REQUIRES(mLock) {
         scoped_unlock unlock(mLock);
-        mPolicy->notifyMonitorResponsive(pid);
-    };
-    postCommandLocked(std::move(command));
-}
-
-void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
-    auto command = [this, connectionToken]() REQUIRES(mLock) {
-        scoped_unlock unlock(mLock);
-        mPolicy->notifyWindowResponsive(connectionToken);
+        mPolicy->notifyWindowResponsive(token, pid);
     };
     postCommandLocked(std::move(command));
 }
@@ -5857,22 +5873,21 @@
 void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
                                                           std::string reason) {
     const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+    std::optional<int32_t> pid;
     if (connection.monitor) {
         ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
               reason.c_str());
-        std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
-        if (!pid.has_value()) {
-            ALOGE("Could not find unresponsive monitor for connection %s",
-                  connection.inputChannel->getName().c_str());
-            return;
+        pid = findMonitorPidByTokenLocked(connectionToken);
+    } else {
+        // The connection is a window
+        ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+              reason.c_str());
+        const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+        if (handle != nullptr) {
+            pid = handle->getInfo()->ownerPid;
         }
-        sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
-        return;
     }
-    // If not a monitor, must be a window
-    ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
-          reason.c_str());
-    sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
+    sendWindowUnresponsiveCommandLocked(connectionToken, pid, std::move(reason));
 }
 
 /**
@@ -5880,18 +5895,17 @@
  */
 void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
     const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+    std::optional<int32_t> pid;
     if (connection.monitor) {
-        std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
-        if (!pid.has_value()) {
-            ALOGE("Could not find responsive monitor for connection %s",
-                  connection.inputChannel->getName().c_str());
-            return;
+        pid = findMonitorPidByTokenLocked(connectionToken);
+    } else {
+        // The connection is a window
+        const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+        if (handle != nullptr) {
+            pid = handle->getInfo()->ownerPid;
         }
-        sendMonitorResponsiveCommandLocked(pid.value());
-        return;
     }
-    // If not a monitor, must be a window
-    sendWindowResponsiveCommandLocked(connectionToken);
+    sendWindowResponsiveCommandLocked(connectionToken, pid);
 }
 
 bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
@@ -6274,13 +6288,14 @@
 
 bool InputDispatcher::shouldDropInput(
         const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
-    if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
-        (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
+    if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) ||
+        (windowHandle->getInfo()->inputConfig.test(
+                 WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
          isWindowObscuredLocked(windowHandle))) {
-        ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
-              "%" PRId32 ".",
+        ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
+              "display %" PRId32 ".",
               ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
-              windowHandle->getInfo()->inputFeatures.string().c_str(),
+              windowHandle->getInfo()->inputConfig.string().c_str(),
               windowHandle->getInfo()->displayId);
         return true;
     }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e162c78..3c79c98 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -84,6 +84,8 @@
     static constexpr bool kDefaultInTouchMode = true;
 
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+                             std::chrono::nanoseconds staleEventTimeout);
     ~InputDispatcher() override;
 
     void dump(std::string& dump) override;
@@ -471,6 +473,11 @@
      */
     std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
 
+    // Amount of time to allow for an event to be dispatched (measured since its eventTime)
+    // before considering it stale and dropping it.
+    const std::chrono::nanoseconds mStaleEventTimeout;
+    bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
+
     bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
 
     /**
@@ -503,11 +510,11 @@
      */
     void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
 
-    void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
-    void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
+    void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken,
+                                             std::optional<int32_t> pid, std::string reason)
             REQUIRES(mLock);
-    void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
-    void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
+    void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken,
+                                           std::optional<int32_t> pid) REQUIRES(mLock);
 
     // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
     // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 3c1e637..de0b6da 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -51,30 +51,19 @@
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
 
     /* Notifies the system that a window just became unresponsive. This indicates that ANR
-     * should be raised for this window. The window is identified via token.
-     * The string reason contains information about the input event that we haven't received
-     * a response for.
+     * should be raised for this window. The window can be identified via its input token and the
+     * pid of the owner. The string reason contains information about the input event that we
+     * haven't received a response for.
      */
-    virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0;
-    /* Notifies the system that a monitor just became unresponsive. This indicates that ANR
-     * should be raised for this monitor. The monitor is identified via its pid.
-     * The string reason contains information about the input event that we haven't received
-     * a response for.
-     */
-    virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0;
+    virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+                                          const std::string& reason) = 0;
 
     /* Notifies the system that a window just became responsive. This is only called after the
      * window was first marked "unresponsive". This indicates that ANR dialog (if any) should
      * no longer should be shown to the user. The window is eligible to cause a new ANR in the
      * future.
      */
-    virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0;
-    /* Notifies the system that a monitor just became responsive. This is only called after the
-     * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should
-     * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the
-     * future.
-     */
-    virtual void notifyMonitorResponsive(int32_t pid) = 0;
+    virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ffce9f6..91be4a3 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -84,6 +84,30 @@
     ASSERT_FALSE(changes);
 }
 
+TEST(FocusResolverTest, RemoveFocusFromFocusedWindow) {
+    sp<IBinder> focusableWindowToken = new BBinder();
+    std::vector<sp<WindowInfoHandle>> windows;
+    windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+                                           true /* visible */));
+
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = focusableWindowToken;
+    FocusResolver focusResolver;
+    // Focusable window gets focus.
+    request.token = focusableWindowToken;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FOCUS_CHANGE(changes, nullptr, focusableWindowToken);
+
+    // Window token of a request is null, focus should be revoked.
+    request.token = NULL;
+    changes = focusResolver.setFocusedWindow(request, windows);
+    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+    ASSERT_EQ(nullptr, changes->newFocus);
+    ASSERT_FOCUS_CHANGE(changes, focusableWindowToken, nullptr);
+}
+
 TEST(FocusResolverTest, SetFocusedMirroredWindow) {
     sp<IBinder> focusableWindowToken = new BBinder();
     sp<IBinder> invisibleWindowToken = new BBinder();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ae8358a..9633932 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -53,6 +53,11 @@
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 static constexpr int32_t SECOND_DISPLAY_ID = 1;
 
+static constexpr int32_t POINTER_1_DOWN =
+        AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_1_UP =
+        AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
@@ -60,6 +65,8 @@
 // An arbitrary pid of the gesture monitor window
 static constexpr int32_t MONITOR_PID = 2001;
 
+static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+
 struct PointF {
     float x;
     float y;
@@ -88,6 +95,8 @@
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
     InputDispatcherConfiguration mConfig;
 
+    using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
+
 protected:
     virtual ~FakeInputDispatcherPolicy() {}
 
@@ -161,122 +170,71 @@
     void assertNotifyNoFocusedWindowAnrWasCalled(
             std::chrono::nanoseconds timeout,
             const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
         std::shared_ptr<InputApplicationHandle> application;
-        { // acquire lock
-            std::unique_lock lock(mLock);
-            android::base::ScopedLockAssertion assumeLocked(mLock);
-            ASSERT_NO_FATAL_FAILURE(
-                    application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
-        } // release lock
+        ASSERT_NO_FATAL_FAILURE(
+                application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
         ASSERT_EQ(expectedApplication, application);
     }
 
     void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
-                                                 const sp<IBinder>& expectedConnectionToken) {
-        sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout);
-        ASSERT_EQ(expectedConnectionToken, connectionToken);
+                                                 const sp<WindowInfoHandle>& window) {
+        LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null");
+        assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(),
+                                                window->getInfo()->ownerPid);
     }
 
-    void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
-        sp<IBinder> connectionToken = getResponsiveWindowToken();
-        ASSERT_EQ(expectedConnectionToken, connectionToken);
+    void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+                                                 const sp<IBinder>& expectedToken,
+                                                 int32_t expectedPid) {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        AnrResult result;
+        ASSERT_NO_FATAL_FAILURE(result =
+                                        getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
+        const auto& [token, pid] = result;
+        ASSERT_EQ(expectedToken, token);
+        ASSERT_EQ(expectedPid, pid);
     }
 
-    void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) {
-        int32_t pid = getUnresponsiveMonitorPid(timeout);
-        ASSERT_EQ(MONITOR_PID, pid);
-    }
-
-    void assertNotifyMonitorResponsiveWasCalled() {
-        int32_t pid = getResponsiveMonitorPid();
-        ASSERT_EQ(MONITOR_PID, pid);
-    }
-
+    /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
     sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock);
+        AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock);
+        const auto& [token, _] = result;
+        return token;
     }
 
+    void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
+                                               int32_t expectedPid) {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        AnrResult result;
+        ASSERT_NO_FATAL_FAILURE(
+                result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
+        const auto& [token, pid] = result;
+        ASSERT_EQ(expectedToken, token);
+        ASSERT_EQ(expectedPid, pid);
+    }
+
+    /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
     sp<IBinder> getResponsiveWindowToken() {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock);
-    }
-
-    int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) {
-        std::unique_lock lock(mLock);
-        android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock);
-    }
-
-    int32_t getResponsiveMonitorPid() {
-        std::unique_lock lock(mLock);
-        android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock);
-    }
-
-    // All three ANR-related callbacks behave the same way, so we use this generic function to wait
-    // for a specific container to become non-empty. When the container is non-empty, return the
-    // first entry from the container and erase it.
-    template <class T>
-    T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
-                                     std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
-        // If there is an ANR, Dispatcher won't be idle because there are still events
-        // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
-        // before checking if ANR was called.
-        // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
-        // to provide it some time to act. 100ms seems reasonable.
-        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
-        const std::chrono::time_point start = std::chrono::steady_clock::now();
-        std::optional<T> token =
-                getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
-        if (!token.has_value()) {
-            ADD_FAILURE() << "Did not receive the ANR callback";
-            return {};
-        }
-
-        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
-        // Ensure that the ANR didn't get raised too early. We can't be too strict here because
-        // the dispatcher started counting before this function was called
-        if (std::chrono::abs(timeout - waited) > 100ms) {
-            ADD_FAILURE() << "ANR was raised too early or too late. Expected "
-                          << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
-                          << "ms, but waited "
-                          << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
-                          << "ms instead";
-        }
-        return *token;
-    }
-
-    template <class T>
-    std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
-                                                           std::queue<T>& storage,
-                                                           std::unique_lock<std::mutex>& lock,
-                                                           std::condition_variable& condition)
-            REQUIRES(mLock) {
-        condition.wait_for(lock, timeout,
-                           [&storage]() REQUIRES(mLock) { return !storage.empty(); });
-        if (storage.empty()) {
-            ADD_FAILURE() << "Did not receive the expected callback";
-            return std::nullopt;
-        }
-        T item = storage.front();
-        storage.pop();
-        return std::make_optional(item);
+        AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock);
+        const auto& [token, _] = result;
+        return token;
     }
 
     void assertNotifyAnrWasNotCalled() {
         std::scoped_lock lock(mLock);
         ASSERT_TRUE(mAnrApplications.empty());
-        ASSERT_TRUE(mAnrWindowTokens.empty());
-        ASSERT_TRUE(mAnrMonitorPids.empty());
-        ASSERT_TRUE(mResponsiveWindowTokens.empty())
+        ASSERT_TRUE(mAnrWindows.empty());
+        ASSERT_TRUE(mResponsiveWindows.empty())
                 << "ANR was not called, but please also consume the 'connection is responsive' "
                    "signal";
-        ASSERT_TRUE(mResponsiveMonitorPids.empty())
-                << "Monitor ANR was not called, but please also consume the 'monitor is responsive'"
-                   " signal";
     }
 
     void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -344,10 +302,8 @@
 
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
-    std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
-    std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock);
-    std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
-    std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
+    std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock);
+    std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
     std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
     std::condition_variable mNotifyInputChannelBroken;
@@ -355,32 +311,74 @@
     sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
     bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
 
+    // All three ANR-related callbacks behave the same way, so we use this generic function to wait
+    // for a specific container to become non-empty. When the container is non-empty, return the
+    // first entry from the container and erase it.
+    template <class T>
+    T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+                                     std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
+        // If there is an ANR, Dispatcher won't be idle because there are still events
+        // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+        // before checking if ANR was called.
+        // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+        // to provide it some time to act. 100ms seems reasonable.
+        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+        const std::chrono::time_point start = std::chrono::steady_clock::now();
+        std::optional<T> token =
+                getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+        if (!token.has_value()) {
+            ADD_FAILURE() << "Did not receive the ANR callback";
+            return {};
+        }
+
+        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+        // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+        // the dispatcher started counting before this function was called
+        if (std::chrono::abs(timeout - waited) > 100ms) {
+            ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+                          << "ms, but waited "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+                          << "ms instead";
+        }
+        return *token;
+    }
+
+    template <class T>
+    std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+                                                           std::queue<T>& storage,
+                                                           std::unique_lock<std::mutex>& lock,
+                                                           std::condition_variable& condition)
+            REQUIRES(mLock) {
+        condition.wait_for(lock, timeout,
+                           [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+        if (storage.empty()) {
+            ADD_FAILURE() << "Did not receive the expected callback";
+            return std::nullopt;
+        }
+        T item = storage.front();
+        storage.pop();
+        return std::make_optional(item);
+    }
+
     void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override {
+    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+                                  const std::string&) override {
         std::scoped_lock lock(mLock);
-        mAnrWindowTokens.push(connectionToken);
+        ASSERT_TRUE(pid.has_value());
+        mAnrWindows.push({connectionToken, *pid});
         mNotifyAnr.notify_all();
     }
 
-    void notifyMonitorUnresponsive(int32_t pid, const std::string&) override {
+    void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+                                std::optional<int32_t> pid) override {
         std::scoped_lock lock(mLock);
-        mAnrMonitorPids.push(pid);
-        mNotifyAnr.notify_all();
-    }
-
-    void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {
-        std::scoped_lock lock(mLock);
-        mResponsiveWindowTokens.push(connectionToken);
-        mNotifyAnr.notify_all();
-    }
-
-    void notifyMonitorResponsive(int32_t pid) override {
-        std::scoped_lock lock(mLock);
-        mResponsiveMonitorPids.push(pid);
+        ASSERT_TRUE(pid.has_value());
+        mResponsiveWindows.push({connectionToken, *pid});
         mNotifyAnr.notify_all();
     }
 
@@ -493,7 +491,7 @@
 
     void SetUp() override {
         mFakePolicy = new FakeInputDispatcherPolicy();
-        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
+        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
         // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
@@ -587,11 +585,10 @@
 
     // Rejects pointer down with invalid index.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
-                     AMOTION_EVENT_ACTION_POINTER_DOWN |
-                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
+                     POINTER_1_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -612,11 +609,10 @@
 
     // Rejects pointer up with invalid index.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
-                     AMOTION_EVENT_ACTION_POINTER_UP |
-                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
+                     POINTER_1_UP, 0, 0, edgeFlags, metaState, 0, classification, identityTransform,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -1000,7 +996,7 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = displayId;
-        mInfo.inputConfig = WindowInfo::InputConfig::NONE;
+        mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
     }
 
     sp<FakeWindowHandle> clone(
@@ -1044,6 +1040,24 @@
         mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
     }
 
+    void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
+
+    void setInterceptsStylus(bool interceptsStylus) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
+    }
+
+    void setDropInput(bool dropInput) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
+    }
+
+    void setDropInputIfObscured(bool dropInputIfObscured) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
+    }
+
+    void setNoInputChannel(bool noInputChannel) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
+    }
+
     void setAlpha(float alpha) { mInfo.alpha = alpha; }
 
     void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1074,8 +1088,6 @@
         mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
     }
 
-    void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
-
     void setTrustedOverlay(bool trustedOverlay) {
         mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
     }
@@ -1228,7 +1240,7 @@
 
     void assertNoEvents() {
         if (mInputReceiver == nullptr &&
-            mInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+            mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
             return; // Can't receive events if the window does not have input channel
         }
         ASSERT_NE(nullptr, mInputReceiver)
@@ -1245,6 +1257,8 @@
         mInfo.ownerUid = ownerUid;
     }
 
+    int32_t getPid() const { return mInfo.ownerPid; }
+
     void destroyReceiver() { mInputReceiver = nullptr; }
 
     int getChannelFd() { return mInputReceiver->getChannelFd(); }
@@ -1503,6 +1517,10 @@
     return args;
 }
 
+static NotifyMotionArgs generateTouchArgs(int32_t action, const std::vector<PointF>& points) {
+    return generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, points);
+}
+
 static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
     return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
 }
@@ -1738,9 +1756,7 @@
 
     // Second finger down on the top window
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
                                      .x(100)
@@ -1803,9 +1819,7 @@
 
     // Second finger down on the right window
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
                                      .x(100)
@@ -1853,6 +1867,61 @@
     wallpaperWindow->assertNoEvents();
 }
 
+/**
+ * On the display, have a single window, and also an area where there's no window.
+ * First pointer touches the "no window" area of the screen. Second pointer touches the window.
+ * Make sure that the window receives the second pointer, and first pointer is simply ignored.
+ */
+TEST_F(InputDispatcherTest, SplitWorksWhenEmptyAreaIsTouched) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Window", DISPLAY_ID);
+
+    mDispatcher->setInputWindows({{DISPLAY_ID, {window}}});
+    NotifyMotionArgs args;
+
+    // Touch down on the empty space
+    mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}})));
+
+    mDispatcher->waitForIdle();
+    window->assertNoEvents();
+
+    // Now touch down on the window with another pointer
+    mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{-1, -1}, {10, 10}})));
+    mDispatcher->waitForIdle();
+    window->consumeMotionDown();
+}
+
+/**
+ * Same test as above, but instead of touching the empty space, the first touch goes to
+ * non-touchable window.
+ */
+TEST_F(InputDispatcherTest, SplitWorksWhenNonTouchableWindowIsTouched) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window1 =
+            new FakeWindowHandle(application, mDispatcher, "Window1", DISPLAY_ID);
+    window1->setTouchableRegion(Region{{0, 0, 100, 100}});
+    window1->setTouchable(false);
+    sp<FakeWindowHandle> window2 =
+            new FakeWindowHandle(application, mDispatcher, "Window2", DISPLAY_ID);
+    window2->setTouchableRegion(Region{{100, 0, 200, 100}});
+
+    mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+
+    NotifyMotionArgs args;
+    // Touch down on the non-touchable window
+    mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})));
+
+    mDispatcher->waitForIdle();
+    window1->assertNoEvents();
+    window2->assertNoEvents();
+
+    // Now touch down on the window with another pointer
+    mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
+    mDispatcher->waitForIdle();
+    window2->consumeMotionDown();
+}
+
 TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> windowLeft =
@@ -2323,9 +2392,7 @@
 
     // Send pointer down to the first window
     NotifyMotionArgs pointerDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {touchPoint, touchPoint});
     mDispatcher->notifyMotion(&pointerDownMotionArgs);
     // Only the first window should get the pointer down event
@@ -2343,9 +2410,7 @@
 
     // Send pointer up to the second window
     NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {touchPoint, touchPoint});
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
     // The first window gets nothing and the second gets pointer up
@@ -2405,9 +2470,7 @@
 
     // Send down to the second window
     NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&secondDownMotionArgs);
     // The first window gets a move and the second a down
@@ -2422,9 +2485,7 @@
 
     // Send pointer up to the second window
     NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
     // The first window gets nothing and the second gets pointer up
@@ -2473,9 +2534,7 @@
 
     // Send down to the second window
     NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&secondDownMotionArgs);
     // The first window gets a move and the second a down
@@ -2492,9 +2551,7 @@
     // The rest of the dispatch should proceed as normal
     // Send pointer up to the second window
     NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
     // The first window gets MOVE and the second gets pointer up
@@ -2711,9 +2768,7 @@
 
     // Send down to the second window
     NotifyMotionArgs secondDownMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&secondDownMotionArgs);
     // The first window gets a move and the second a down
@@ -2722,9 +2777,7 @@
 
     // Send pointer cancel to the second window
     NotifyMotionArgs pointerUpMotionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+            generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {pointInFirst, pointInSecond});
     pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
@@ -4180,22 +4233,18 @@
     touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
-    int32_t actionPointerDown =
-            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
     touchedPoints.push_back(PointF{150, 150});
     expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
 
     // Release Window 2
-    int32_t actionPointerUp =
-            AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-    touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
     expectedPoints.pop_back();
 
     // Update the transform so rotation is set for Window 2
     mWindow2->setWindowTransform(0, -1, 1, 0);
     expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
 }
 
 TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
@@ -4207,12 +4256,10 @@
     touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
-    int32_t actionPointerDown =
-            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
     touchedPoints.push_back(PointF{150, 150});
     expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
 
-    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
 
     // Move both windows
     touchedPoints = {{20, 20}, {175, 175}};
@@ -4222,15 +4269,13 @@
     touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
 
     // Release Window 2
-    int32_t actionPointerUp =
-            AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-    touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
     expectedPoints.pop_back();
 
     // Touch Window 2
     mWindow2->setWindowTransform(0, -1, 1, 0);
     expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
 
     // Move both windows
     touchedPoints = {{20, 20}, {175, 175}};
@@ -4249,12 +4294,10 @@
     touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
-    int32_t actionPointerDown =
-            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
     touchedPoints.push_back(PointF{150, 150});
     expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
 
-    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+    touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
 
     // Move both windows
     touchedPoints = {{20, 20}, {175, 175}};
@@ -4308,7 +4351,7 @@
                 new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
         spy->setTrustedOverlay(true);
         spy->setFocusable(false);
-        spy->setInputFeatures(WindowInfo::Feature::SPY);
+        spy->setSpy(true);
         spy->setDispatchingTimeout(30ms);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}});
         return spy;
@@ -4364,13 +4407,13 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
 
     mWindow->finishEvent(*sequenceNum);
     mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
 }
 
 // Send a key to the app and have the app not respond right away.
@@ -4380,7 +4423,7 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
@@ -4410,6 +4453,38 @@
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
+/**
+ * Make sure the stale key is dropped before causing an ANR. So even if there's no focused window,
+ * there will not be an ANR.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    KeyEvent event;
+    const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) -
+            std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
+
+    // Define a valid key down event that is stale (too old).
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+                     AMETA_NONE, 1 /*repeatCount*/, eventTime, eventTime);
+
+    const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+
+    InputEventInjectionResult result =
+            mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                          InputEventInjectionSync::WAIT_FOR_RESULT,
+                                          INJECT_EVENT_TIMEOUT, policyFlags);
+    ASSERT_EQ(InputEventInjectionResult::FAILED, result)
+            << "Injection should fail because the event is stale";
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+    mWindow->assertNoEvents();
+}
+
 // We have a focused application, but no focused window
 // Make sure that we don't notify policy twice about the same ANR.
 TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
@@ -4485,7 +4560,7 @@
     // We have now sent down and up. Let's consume first event and then ANR on the second.
     mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
 }
 
 // A spy window can receive an ANR
@@ -4500,13 +4575,13 @@
     std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy);
 
     spy->finishEvent(*sequenceNum);
     spy->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
                       0 /*flags*/);
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken(), mWindow->getPid());
 }
 
 // If an app is not responding to a key event, spy windows should continue to receive
@@ -4521,7 +4596,7 @@
 
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
 
     // New tap will go to the spy window, but not to the window
     tapOnWindow();
@@ -4530,7 +4605,7 @@
 
     mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
     mWindow->assertNoEvents();
     spy->assertNoEvents();
 }
@@ -4547,7 +4622,7 @@
     mWindow->consumeMotionDown();
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
 
     // New tap will go to the spy window, but not to the window
     tapOnWindow();
@@ -4556,7 +4631,7 @@
 
     mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
     mWindow->assertNoEvents();
     spy->assertNoEvents();
 }
@@ -4574,13 +4649,13 @@
     const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
     ASSERT_TRUE(consumeSeq);
 
-    mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(30ms);
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(30ms, monitor.getToken(), MONITOR_PID);
 
     monitor.finishEvent(*consumeSeq);
     monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(monitor.getToken(), MONITOR_PID);
 }
 
 // If a window is unresponsive, then you get anr. if the window later catches up and starts to
@@ -4595,19 +4670,19 @@
     mWindow->consumeMotionDown();
     // Block on ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
     mWindow->consumeMotionUp(); // Now the connection should be healthy again
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
     mWindow->assertNoEvents();
 
     tapOnWindow();
     mWindow->consumeMotionDown();
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
     mWindow->consumeMotionUp();
 
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
     mFakePolicy->assertNotifyAnrWasNotCalled();
     mWindow->assertNoEvents();
 }
@@ -4620,7 +4695,7 @@
                                WINDOW_LOCATION));
 
     const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow);
     std::this_thread::sleep_for(windowTimeout);
     // 'notifyConnectionUnresponsive' should only be called once per connection
     mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -4630,7 +4705,7 @@
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     mWindow->assertNoEvents();
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -4792,7 +4867,7 @@
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
     // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
     // sequence to make it consistent
     mFocusedWindow->consumeMotionCancel();
@@ -4803,7 +4878,8 @@
     mFocusedWindow->assertNoEvents();
     mUnfocusedWindow->assertNoEvents();
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+                                                       mFocusedWindow->getPid());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -4817,8 +4893,9 @@
 
     tapOnFocusedWindow();
     // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
-    sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms);
-    sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms);
+    sp<IBinder> anrConnectionToken1, anrConnectionToken2;
+    ASSERT_NO_FATAL_FAILURE(anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms));
+    ASSERT_NO_FATAL_FAILURE(anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms));
 
     // We don't know which window will ANR first. But both of them should happen eventually.
     ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
@@ -4833,8 +4910,9 @@
     mFocusedWindow->consumeMotionUp();
     mUnfocusedWindow->consumeMotionOutside();
 
-    sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken();
-    sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken();
+    sp<IBinder> responsiveToken1, responsiveToken2;
+    ASSERT_NO_FATAL_FAILURE(responsiveToken1 = mFakePolicy->getResponsiveWindowToken());
+    ASSERT_NO_FATAL_FAILURE(responsiveToken2 = mFakePolicy->getResponsiveWindowToken());
 
     // Both applications should be marked as responsive, in any order
     ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
@@ -4858,7 +4936,7 @@
     ASSERT_TRUE(upEventSequenceNum);
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
 
     // Tap once again
     // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -4879,7 +4957,8 @@
     // The second tap did not go to the focused window
     mFocusedWindow->assertNoEvents();
     // Since all events are finished, connection should be deemed healthy again
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+                                                       mFocusedWindow->getPid());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -4981,17 +5060,13 @@
                                    ADISPLAY_ID_DEFAULT, 0 /*flags*/);
 
     // Touch Window 2
-    int32_t actionPointerDown =
-            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-
-    motionArgs =
-            generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+    motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                    {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
     mDispatcher->notifyMotion(&motionArgs);
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
 
     mUnfocusedWindow->consumeMotionDown();
     mFocusedWindow->consumeMotionDown();
@@ -5010,7 +5085,8 @@
         ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
     }
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken(),
+                                                       mFocusedWindow->getPid());
 
     mUnfocusedWindow->assertNoEvents();
     mFocusedWindow->assertNoEvents();
@@ -5096,7 +5172,7 @@
                                               "Window without input channel", ADISPLAY_ID_DEFAULT,
                                               std::make_optional<sp<IBinder>>(nullptr) /*token*/);
 
-        mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
+        mNoInputWindow->setNoInputChannel(true);
         mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
         // It's perfectly valid for this window to not have an associated input channel
 
@@ -5138,7 +5214,7 @@
                                           "Window with input channel and NO_INPUT_CHANNEL",
                                           ADISPLAY_ID_DEFAULT);
 
-    mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
+    mNoInputWindow->setNoInputChannel(true);
     mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
 
@@ -6053,7 +6129,7 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
-    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
+    window->setDropInput(true);
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -6072,7 +6148,7 @@
     window->assertNoEvents();
 
     // With the flag cleared, the window should get input
-    window->setInputFeatures({});
+    window->setDropInput(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
     keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
@@ -6098,7 +6174,7 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
-    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+    window->setDropInputIfObscured(true);
     window->setOwnerInfo(222, 222);
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
     window->setFocusable(true);
@@ -6118,7 +6194,7 @@
     window->assertNoEvents();
 
     // With the flag cleared, the window should get input
-    window->setInputFeatures({});
+    window->setDropInputIfObscured(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
 
     keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
@@ -6144,7 +6220,7 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
-    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+    window->setDropInputIfObscured(true);
     window->setOwnerInfo(222, 222);
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
     window->setFocusable(true);
@@ -6253,7 +6329,7 @@
         name += std::to_string(mSpyCount++);
         sp<FakeWindowHandle> spy =
                 new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
-        spy->setInputFeatures(WindowInfo::Feature::SPY);
+        spy->setSpy(true);
         spy->setTrustedOverlay(true);
         return spy;
     }
@@ -6511,9 +6587,7 @@
 
     // Second finger down on the window and spy, but the window should not receive the pointer down.
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .displayId(ADISPLAY_ID_DEFAULT)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
@@ -6571,9 +6645,7 @@
     spy->consumeMotionDown();
 
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
                     .pointer(
@@ -6606,9 +6678,7 @@
     spyRight->assertNoEvents();
 
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
                     .pointer(
@@ -6647,9 +6717,7 @@
 
     // Second finger down on window, the window should receive touch down.
     const MotionEvent secondFingerDownEvent =
-            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
-                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                               AINPUT_SOURCE_TOUCHSCREEN)
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                     .displayId(ADISPLAY_ID_DEFAULT)
                     .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                     .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
@@ -6701,7 +6769,7 @@
         overlay->setFocusable(false);
         overlay->setOwnerInfo(111, 111);
         overlay->setTouchable(false);
-        overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
+        overlay->setInterceptsStylus(true);
         overlay->setTrustedOverlay(true);
 
         std::shared_ptr<FakeApplicationHandle> application =
@@ -6767,7 +6835,7 @@
 
 TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
     auto [overlay, window] = setupStylusOverlayScenario();
-    overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY);
+    overlay->setSpy(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
 
     sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index df4db19..455a1e2 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -42,4 +42,7 @@
     srcs: [
         "LatencyTrackerFuzzer.cpp",
     ],
+    fuzz_config: {
+       cc: ["android-framework-input@google.com"],
+    },
 }
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 86c8b0d..4d1de96 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -232,7 +232,7 @@
                     break;
                 }
                 default: {
-                    ALOGE("Invalid sensor additional info tag: %d", srcInfo.payload.getTag());
+                    ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
                 }
             }
             break;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index d9958f3..000a2cb 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -76,6 +76,7 @@
         "libaidlcommonsupport",
         "libcompositionengine",
         "libframetimeline",
+        "libgui_aidl_static",
         "libperfetto_client_experimental",
         "librenderengine",
         "libscheduler",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 18a6bae..0aca24a 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -311,6 +311,7 @@
             ? 0
             : mBufferInfo.mBufferSlot;
     compositionState->acquireFence = mBufferInfo.mFence;
+    compositionState->frameNumber = mBufferInfo.mFrameNumber;
     compositionState->sidebandStreamHasFrame = false;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 02e444d..e6a76e8 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -271,7 +271,8 @@
 // Translate destination frame into scale and position. If a destination frame is not set, use the
 // provided scale and position
 bool BufferStateLayer::updateGeometry() {
-    if (mDrawingState.destinationFrame.isEmpty()) {
+    if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
+        mDrawingState.destinationFrame.isEmpty()) {
         // If destination frame is not set, use the requested transform set via
         // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
         return assignTransform(&mDrawingState.transform, mRequestedTransform);
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 669eaad..8a696f1 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -141,6 +141,8 @@
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
+    uint64_t mPreviousBarrierFrameNumber = 0;
+
     bool mReleasePreviousBuffer = false;
 
     // Stores the last set acquire fence signal time used to populate the callback handle's acquire
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..9302b7b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -41,6 +41,10 @@
         "libtonemap",
         "libtrace_proto",
         "libaidlcommonsupport",
+        "libprocessgroup",
+        "libcgrouprc",
+        "libjsoncpp",
+        "libcgrouprc_format",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -68,6 +72,7 @@
         "src/DisplayColorProfile.cpp",
         "src/DisplaySurface.cpp",
         "src/DumpHelpers.cpp",
+        "src/HwcAsyncWorker.cpp",
         "src/HwcBufferCache.cpp",
         "src/LayerFECompositionState.cpp",
         "src/Output.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 47aacc9..6a3fcb7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -57,7 +57,7 @@
     virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
     // Returns the boot display mode preferred by HWC.
-    virtual int32_t getPreferredBootModeId() const = 0;
+    virtual int32_t getPreferredBootHwcConfigId() const = 0;
 
 protected:
     ~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index c553fce..ca86f4c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -72,6 +72,9 @@
     virtual void resizeBuffers(const ui::Size&) = 0;
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+    // Returns true if the render surface supports client composition prediction.
+    virtual bool supportsCompositionStrategyPrediction() const;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8bf7f8f..283fe86 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -166,6 +166,7 @@
     int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
     sp<Fence> acquireFence = Fence::NO_FENCE;
     Region surfaceDamage;
+    uint64_t frameNumber = 0;
 
     // The handle to use for a sideband stream for this layer
     sp<NativeHandle> sidebandStream;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d8644a4..1555102 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -35,6 +35,7 @@
 #include <utils/Vector.h>
 
 #include <ui/DisplayIdentification.h>
+#include "DisplayHardware/HWComposer.h"
 
 namespace android {
 
@@ -54,6 +55,7 @@
 
 namespace impl {
 struct OutputCompositionState;
+struct GpuCompositionResult;
 } // namespace impl
 
 /**
@@ -262,6 +264,9 @@
     // Latches the front-end layer state for each output layer
     virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
 
+    // Enables predicting composition strategy to run client composition earlier
+    virtual void setPredictCompositionStrategy(bool) = 0;
+
 protected:
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
@@ -278,13 +283,22 @@
     virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
     virtual void beginFrame() = 0;
     virtual void prepareFrame() = 0;
+
+    using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
+    // Runs prepare frame in another thread while running client composition using
+    // the previous frame's composition strategy.
+    virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
     virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
-    virtual void finishFrame(const CompositionRefreshArgs&) = 0;
+    virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
     virtual std::optional<base::unique_fd> composeSurfaces(
-            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
+            const Region&, const compositionengine::CompositionRefreshArgs&,
+            std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
     virtual void postFramebuffer() = 0;
     virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
-    virtual void chooseCompositionStrategy() = 0;
+    virtual std::optional<android::HWComposer::DeviceRequestedChanges>
+    chooseCompositionStrategy() = 0;
+    virtual void applyCompositionStrategy(
+            const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentAndGetFrameFences() = 0;
     virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -295,6 +309,7 @@
             std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
     virtual void setExpensiveRenderingExpected(bool enabled) = 0;
     virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
+    virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index daee83b..9ee779c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -100,6 +100,9 @@
 
     // Debugging - gets the page flip count for the RenderSurface
     virtual std::uint32_t getPageFlipCount() const = 0;
+
+    // Returns true if the render surface supports client composition prediction.
+    virtual bool supportsCompositionStrategyPrediction() const = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index c7984bd..3b8b06f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -22,6 +22,7 @@
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
 #include <compositionengine/impl/Output.h>
 #include <ui/PixelFormat.h>
 #include <ui/Size.h>
@@ -51,18 +52,21 @@
     void setReleasedLayers(const CompositionRefreshArgs&) override;
     void setColorTransform(const CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
-    void chooseCompositionStrategy() override;
+
+    using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+    std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+    void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     void setExpensiveRenderingExpected(bool) override;
-    void finishFrame(const CompositionRefreshArgs&) override;
+    void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
 
     // compositionengine::Display overrides
     DisplayId getId() const override;
     bool isSecure() const override;
     bool isVirtual() const override;
     void disconnect() override;
-    int32_t getPreferredBootModeId() const override;
+    int32_t getPreferredBootHwcConfigId() const override;
     void createDisplayColorProfile(
             const compositionengine::DisplayColorProfileCreationArgs&) override;
     void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
@@ -73,7 +77,6 @@
     using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
     using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
     using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
-    virtual bool anyLayersRequireClientComposition() const;
     virtual bool allLayersRequireClientComposition() const;
     virtual void applyChangedTypesToLayers(const ChangedTypes&);
     virtual void applyDisplayRequests(const DisplayRequests&);
@@ -88,7 +91,7 @@
     DisplayId mId;
     bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
-    int32_t mPreferredBootDisplayModeId = -1;
+    int32_t mPreferredBootHwcConfigId = -1;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
new file mode 100644
index 0000000..ed1ddc1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine::impl {
+
+struct GpuCompositionResult {
+    // True if composition strategy was predicted successfully.
+    bool succeeded = false;
+
+    // Composition ready fence.
+    base::unique_fd fence{};
+
+    // Buffer to be used for gpu composition. If gpu composition was not successful,
+    // then we want to reuse the buffer instead of dequeuing another buffer.
+    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+
+    bool bufferAvailable() const { return buffer != nullptr; };
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
new file mode 100644
index 0000000..11c0054
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <future>
+#include <optional>
+#include <thread>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+// HWC Validate call may take multiple milliseconds to complete and can account for
+// a signification amount of time in the display hotpath. This helper class allows
+// us to run the hwc validate function on a real time thread if we can predict what
+// the composition strategy will be and if composition includes client composition.
+// While the hwc validate runs, client composition is kicked off with the prediction.
+// When the worker returns with a value, the composition continues if the prediction
+// was successful otherwise the client composition is re-executed.
+//
+// Note: This does not alter the sequence between HWC and surfaceflinger.
+class HwcAsyncWorker final {
+public:
+    HwcAsyncWorker();
+    ~HwcAsyncWorker();
+    // Runs the provided function which calls hwc validate and returns the requested
+    // device changes as a future.
+    std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> send(
+            std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()>);
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCv GUARDED_BY(mMutex);
+    bool mDone GUARDED_BY(mMutex) = false;
+    bool mTaskRequested GUARDED_BY(mMutex) = false;
+    std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()> mTask
+            GUARDED_BY(mMutex);
+    std::thread mThread;
+    void run();
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a7a8e97..0be5d01 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -17,9 +17,13 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
@@ -92,25 +96,38 @@
     void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
     void beginFrame() override;
     void prepareFrame() override;
+    GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
     void devOptRepaintFlash(const CompositionRefreshArgs&) override;
-    void finishFrame(const CompositionRefreshArgs&) override;
-    std::optional<base::unique_fd> composeSurfaces(
-            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
+    void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+    std::optional<base::unique_fd> composeSurfaces(const Region&,
+                                                   const compositionengine::CompositionRefreshArgs&,
+                                                   std::shared_ptr<renderengine::ExternalTexture>,
+                                                   base::unique_fd&) override;
     void postFramebuffer() override;
     void renderCachedSets(const CompositionRefreshArgs&) override;
     void cacheClientCompositionRequests(uint32_t) override;
+    bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
+    void setPredictCompositionStrategy(bool) override;
 
     // Testing
     const ReleasedLayers& getReleasedLayersForTest() const;
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
     bool plannerEnabled() const { return mPlanner != nullptr; }
+    virtual bool anyLayersRequireClientComposition() const;
+    virtual void updateProtectedContentState();
+    virtual bool dequeueRenderBuffer(base::unique_fd*,
+                                     std::shared_ptr<renderengine::ExternalTexture>*);
+    virtual std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+    chooseCompositionStrategyAsync();
 
 protected:
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
     std::optional<size_t> findCurrentOutputLayerForLayer(
             const sp<compositionengine::LayerFE>&) const;
-    void chooseCompositionStrategy() override;
+    using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+    std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+    void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -131,6 +148,7 @@
 private:
     void dirtyEntireOutput();
     compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
+    void finishPrepareFrame();
     ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
     compositionengine::Output::ColorProfile pickColorProfile(
             const compositionengine::CompositionRefreshArgs&) const;
@@ -144,6 +162,7 @@
     OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
     std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
     std::unique_ptr<planner::Planner> mPlanner;
+    std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66dd825..92f22b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -37,6 +37,8 @@
 #include <ui/Region.h>
 #include <ui/Transform.h>
 
+#include "DisplayHardware/HWComposer.h"
+
 namespace android {
 
 namespace compositionengine::impl {
@@ -114,6 +116,8 @@
     // Current target dataspace
     ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
 
+    std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
+
     // The earliest time to send the present command to the HAL
     std::chrono::steady_clock::time_point earliestPresentTime;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index a8a5380..e4cb113 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -63,6 +63,7 @@
     void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
     void flip() override;
+    bool supportsCompositionStrategyPrediction() const override;
 
     // Debugging
     void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 14324de..cb00e71 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,7 @@
 #include "DisplayHardware/Hal.h"
 #include "math/HashCombine.h"
 
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
 namespace std {
@@ -395,6 +396,21 @@
                                 return std::vector<std::string>{base::StringPrintf("%p", p)};
                             }};
 
+    static auto constexpr BufferEquals = [](const wp<GraphicBuffer>& lhs,
+                                            const wp<GraphicBuffer>& rhs) -> bool {
+        // Avoid a promotion if the wp<>'s aren't equal
+        if (lhs != rhs) return false;
+
+        // Even if the buffer didn't change, check to see if we need to act as if the buffer changed
+        // anyway. Specifically, look to see if the buffer is FRONT_BUFFER & if so act as if it's
+        // always different
+        using ::aidl::android::hardware::graphics::common::BufferUsage;
+        sp<GraphicBuffer> promotedBuffer = lhs.promote();
+        return !(promotedBuffer &&
+                 ((promotedBuffer->getUsage() & static_cast<int64_t>(BufferUsage::FRONT_BUFFER)) !=
+                  0));
+    };
+
     OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
             mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
                     [](const wp<GraphicBuffer>& buffer) {
@@ -403,7 +419,14 @@
                                 base::StringPrintf("%p",
                                                    promotedBuffer ? promotedBuffer.get()
                                                                   : nullptr)};
-                    }};
+                    },
+                    BufferEquals};
+
+    // Even if the same buffer is passed to BLAST's setBuffer(), we still increment the frame
+    // number and need to treat it as if the buffer changed. Otherwise we break existing
+    // front-buffer rendering paths (such as egl's EGL_SINGLE_BUFFER).
+    OutputLayerState<uint64_t, LayerStateField::Buffer> mFrameNumber{
+            [](auto layer) { return layer->getLayerFE().getCompositionState()->frameNumber; }};
 
     int64_t mFramesSinceBufferUpdate = 0;
 
@@ -453,7 +476,7 @@
                                       return hash;
                                   }};
 
-    static const constexpr size_t kNumNonUniqueFields = 16;
+    static const constexpr size_t kNumNonUniqueFields = 17;
 
     std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
         std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -472,6 +495,7 @@
                 &mAlpha,        &mLayerMetadata,  &mVisibleRegion,        &mOutputDataspace,
                 &mPixelFormat,  &mColorTransform, &mCompositionType,      &mSidebandStream,
                 &mBuffer,       &mSolidColor,     &mBackgroundBlurRadius, &mBlurRegions,
+                &mFrameNumber,
         };
     }
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 84afb59..72e6f3b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -34,13 +34,14 @@
     MOCK_CONST_METHOD0(getId, DisplayId());
     MOCK_CONST_METHOD0(isSecure, bool());
     MOCK_CONST_METHOD0(isVirtual, bool());
-    MOCK_CONST_METHOD0(getPreferredBootModeId, int32_t());
+    MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t());
 
     MOCK_METHOD0(disconnect, void());
 
     MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
     MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
     MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+    MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index b68b95d..27303a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -22,6 +22,7 @@
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <gmock/gmock.h>
 
@@ -99,16 +100,22 @@
     MOCK_METHOD0(beginFrame, void());
 
     MOCK_METHOD0(prepareFrame, void());
-    MOCK_METHOD0(chooseCompositionStrategy, void());
+    MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+    MOCK_METHOD0(chooseCompositionStrategy,
+                 std::optional<android::HWComposer::DeviceRequestedChanges>());
+    MOCK_METHOD1(applyCompositionStrategy,
+                 void(const std::optional<android::HWComposer::DeviceRequestedChanges>&));
 
     MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+    MOCK_METHOD2(finishFrame,
+                 void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
 
-    MOCK_METHOD2(composeSurfaces,
+    MOCK_METHOD4(composeSurfaces,
                  std::optional<base::unique_fd>(
                          const Region&,
-                         const compositionengine::CompositionRefreshArgs& refreshArgs));
+                         const compositionengine::CompositionRefreshArgs& refreshArgs,
+                         std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
     MOCK_CONST_METHOD0(getSkipColorTransform, bool());
 
     MOCK_METHOD0(postFramebuffer, void());
@@ -121,6 +128,8 @@
                  void(const Region&, std::vector<LayerFE::LayerSettings>&));
     MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
     MOCK_METHOD1(cacheClientCompositionRequests, void(uint32_t));
+    MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+    MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index fe858c2..e12aebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -45,6 +45,7 @@
     MOCK_METHOD0(flip, void());
     MOCK_CONST_METHOD1(dump, void(std::string& result));
     MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+    MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6390025..09648c3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -66,7 +66,7 @@
     std::optional<hal::HWConfigId> preferredBootModeId =
             getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId);
     if (preferredBootModeId.has_value()) {
-        mPreferredBootDisplayModeId = static_cast<int32_t>(preferredBootModeId.value());
+        mPreferredBootHwcConfigId = static_cast<int32_t>(preferredBootModeId.value());
     }
 }
 
@@ -90,8 +90,8 @@
     return mId;
 }
 
-int32_t Display::getPreferredBootModeId() const {
-    return mPreferredBootDisplayModeId;
+int32_t Display::getPreferredBootHwcConfigId() const {
+    return mPreferredBootHwcConfigId;
 }
 
 void Display::disconnect() {
@@ -221,12 +221,12 @@
     setReleasedLayers(std::move(releasedLayers));
 }
 
-void Display::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompositionStrategy() {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
     if (mIsDisconnected) {
-        return;
+        return {};
     }
 
     // Default to the base settings -- client composition only.
@@ -235,7 +235,7 @@
     // If we don't have a HWC display, then we are done.
     const auto halDisplayId = HalDisplayId::tryCast(mId);
     if (!halDisplayId) {
-        return;
+        return {};
     }
 
     // Get any composition changes requested by the HWC device, and apply them.
@@ -260,8 +260,13 @@
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
-        return;
+        return {};
     }
+
+    return changes;
+}
+
+void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChanges>& changes) {
     if (changes) {
         applyChangedTypesToLayers(changes->changedTypes);
         applyDisplayRequests(changes->displayRequests);
@@ -287,12 +292,6 @@
     return hwc.hasCapability(Capability::SKIP_CLIENT_COLOR_TRANSFORM);
 }
 
-bool Display::anyLayersRequireClientComposition() const {
-    const auto layers = getOutputLayersOrderedByZ();
-    return std::any_of(layers.begin(), layers.end(),
-                       [](const auto& layer) { return layer->requiresClientComposition(); });
-}
-
 bool Display::allLayersRequireClientComposition() const {
     const auto layers = getOutputLayersOrderedByZ();
     return std::all_of(layers.begin(), layers.end(),
@@ -390,7 +389,8 @@
     }
 }
 
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                          GpuCompositionResult&& result) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
@@ -400,7 +400,7 @@
         return;
     }
 
-    impl::Output::finishFrame(refreshArgs);
+    impl::Output::finishFrame(refreshArgs, std::move(result));
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index db6d4f2..28900af 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -20,4 +20,8 @@
 
 DisplaySurface::~DisplaySurface() = default;
 
+bool DisplaySurface::supportsCompositionStrategyPrediction() const {
+    return true;
+}
+
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
new file mode 100644
index 0000000..497424a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <processgroup/sched_policy.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <system/thread_defs.h>
+
+#include <android-base/thread_annotations.h>
+#include <cutils/sched_policy.h>
+
+namespace android::compositionengine::impl {
+
+HwcAsyncWorker::HwcAsyncWorker() {
+    mThread = std::thread(&HwcAsyncWorker::run, this);
+    pthread_setname_np(mThread.native_handle(), "HwcAsyncWorker");
+}
+
+HwcAsyncWorker::~HwcAsyncWorker() {
+    {
+        std::scoped_lock lock(mMutex);
+        mDone = true;
+        mCv.notify_all();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> HwcAsyncWorker::send(
+        std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()> task) {
+    std::unique_lock<std::mutex> lock(mMutex);
+    android::base::ScopedLockAssertion assumeLock(mMutex);
+    mTask = std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()>(
+            [task = std::move(task)]() { return task(); });
+    mTaskRequested = true;
+    mCv.notify_one();
+    return mTask.get_future();
+}
+
+void HwcAsyncWorker::run() {
+    set_sched_policy(0, SP_FOREGROUND);
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    sched_setscheduler(gettid(), SCHED_FIFO, &param);
+
+    std::unique_lock<std::mutex> lock(mMutex);
+    android::base::ScopedLockAssertion assumeLock(mMutex);
+    while (!mDone) {
+        mCv.wait(lock);
+        if (mTaskRequested && mTask.valid()) {
+            mTask();
+            mTaskRequested = false;
+        }
+    }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ff332eb..cd10bc1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -22,6 +22,7 @@
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
 #include <compositionengine/impl/Output.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
@@ -434,9 +435,17 @@
     writeCompositionState(refreshArgs);
     setColorTransform(refreshArgs);
     beginFrame();
-    prepareFrame();
+
+    GpuCompositionResult result;
+    const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
+    if (predictCompositionStrategy) {
+        result = prepareFrameAsync(refreshArgs);
+    } else {
+        prepareFrame();
+    }
+
     devOptRepaintFlash(refreshArgs);
-    finishFrame(refreshArgs);
+    finishFrame(refreshArgs, std::move(result));
     postFramebuffer();
     renderCachedSets(refreshArgs);
 }
@@ -951,19 +960,62 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
-    const auto& outputState = getState();
+    auto& outputState = editState();
     if (!outputState.isEnabled) {
         return;
     }
 
-    chooseCompositionStrategy();
+    auto changes = chooseCompositionStrategy();
+    outputState.previousDeviceRequestedChanges = changes;
+    if (changes) {
+        applyCompositionStrategy(changes);
+    }
+    finishPrepareFrame();
+}
 
-    if (mPlanner) {
-        mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+Output::chooseCompositionStrategyAsync() {
+    return mHwComposerAsyncWorker->send([&]() { return chooseCompositionStrategy(); });
+}
+
+GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+    auto& state = editState();
+    const auto& previousChanges = state.previousDeviceRequestedChanges;
+    auto hwcResult = chooseCompositionStrategyAsync();
+    applyCompositionStrategy(previousChanges);
+    finishPrepareFrame();
+
+    base::unique_fd bufferFence;
+    std::shared_ptr<renderengine::ExternalTexture> buffer;
+    updateProtectedContentState();
+    const bool dequeueSucceeded = dequeueRenderBuffer(&bufferFence, &buffer);
+    GpuCompositionResult compositionResult;
+    if (dequeueSucceeded) {
+        std::optional<base::unique_fd> optFd =
+                composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+        if (optFd) {
+            compositionResult.fence = std::move(*optFd);
+        }
     }
 
-    mRenderSurface->prepareFrame(outputState.usesClientComposition,
-                                 outputState.usesDeviceComposition);
+    auto changes = hwcResult.valid() ? hwcResult.get() : std::nullopt;
+    const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges;
+    compositionResult.succeeded = predictionSucceeded;
+    if (!predictionSucceeded) {
+        ATRACE_NAME("CompositionStrategyPredictionMiss");
+        if (changes) {
+            applyCompositionStrategy(changes);
+        }
+        finishPrepareFrame();
+        // Track the dequeued buffer to reuse so we don't need to dequeue another one.
+        compositionResult.buffer = buffer;
+    } else {
+        ATRACE_NAME("CompositionStrategyPredictionHit");
+    }
+    state.previousDeviceRequestedChanges = std::move(changes);
+    return compositionResult;
 }
 
 void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -973,7 +1025,11 @@
 
     if (getState().isEnabled) {
         if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
-            static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
+            base::unique_fd bufferFence;
+            std::shared_ptr<renderengine::ExternalTexture> buffer;
+            updateProtectedContentState();
+            dequeueRenderBuffer(&bufferFence, &buffer);
+            static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
             mRenderSurface->queueBuffer(base::unique_fd());
         }
     }
@@ -985,7 +1041,7 @@
     prepareFrame();
 }
 
-void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
@@ -993,9 +1049,25 @@
         return;
     }
 
-    // Repaint the framebuffer (if needed), getting the optional fence for when
-    // the composition completes.
-    auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);
+    std::optional<base::unique_fd> optReadyFence;
+    std::shared_ptr<renderengine::ExternalTexture> buffer;
+    base::unique_fd bufferFence;
+    if (result.succeeded) {
+        optReadyFence = std::move(result.fence);
+    } else {
+        if (result.bufferAvailable()) {
+            buffer = std::move(result.buffer);
+            bufferFence = std::move(result.fence);
+        } else {
+            updateProtectedContentState();
+            if (!dequeueRenderBuffer(&bufferFence, &buffer)) {
+                return;
+            }
+        }
+        // Repaint the framebuffer (if needed), getting the optional fence for when
+        // the composition completes.
+        optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+    }
     if (!optReadyFence) {
         return;
     }
@@ -1004,16 +1076,8 @@
     mRenderSurface->queueBuffer(std::move(*optReadyFence));
 }
 
-std::optional<base::unique_fd> Output::composeSurfaces(
-        const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) {
-    ATRACE_CALL();
-    ALOGV(__FUNCTION__);
-
+void Output::updateProtectedContentState() {
     const auto& outputState = getState();
-    OutputCompositionState& outputCompositionState = editState();
-    const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
-                                                      outputState.usesClientComposition};
-
     auto& renderEngine = getCompositionEngine().getRenderEngine();
     const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
 
@@ -1035,29 +1099,48 @@
     } else if (!outputState.isSecure && renderEngine.isProtected()) {
         renderEngine.useProtectedContext(false);
     }
+}
 
-    base::unique_fd fd;
-
-    std::shared_ptr<renderengine::ExternalTexture> tex;
+bool Output::dequeueRenderBuffer(base::unique_fd* bufferFence,
+                                 std::shared_ptr<renderengine::ExternalTexture>* tex) {
+    const auto& outputState = getState();
 
     // If we aren't doing client composition on this output, but do have a
     // flipClientTarget request for this frame on this output, we still need to
     // dequeue a buffer.
-    if (hasClientComposition || outputState.flipClientTarget) {
-        tex = mRenderSurface->dequeueBuffer(&fd);
-        if (tex == nullptr) {
+    if (outputState.usesClientComposition || outputState.flipClientTarget) {
+        *tex = mRenderSurface->dequeueBuffer(bufferFence);
+        if (*tex == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
                   "client composition for this frame",
                   mName.c_str());
-            return {};
+            return false;
         }
     }
+    return true;
+}
 
+std::optional<base::unique_fd> Output::composeSurfaces(
+        const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
+        std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    const auto& outputState = getState();
+    const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
+                                                      outputState.usesClientComposition};
     if (!hasClientComposition) {
         setExpensiveRenderingExpected(false);
         return base::unique_fd();
     }
 
+    if (tex == nullptr) {
+        ALOGW("Buffer not valid for display [%s], bailing out of "
+              "client composition for this frame",
+              mName.c_str());
+        return {};
+    }
+
     ALOGV("hasClientComposition");
 
     renderengine::DisplaySettings clientCompositionDisplay;
@@ -1085,6 +1168,8 @@
             outputState.usesDeviceComposition || getSkipColorTransform();
 
     // Generate the client composition requests for the layers on this output.
+    auto& renderEngine = getCompositionEngine().getRenderEngine();
+    const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
     std::vector<LayerFE*> clientCompositionLayersFE;
     std::vector<LayerFE::LayerSettings> clientCompositionLayers =
             generateClientCompositionRequests(supportsProtectedContent,
@@ -1092,16 +1177,19 @@
                                               clientCompositionLayersFE);
     appendRegionFlashRequests(debugRegion, clientCompositionLayers);
 
+    OutputCompositionState& outputCompositionState = editState();
     // Check if the client composition requests were rendered into the provided graphic buffer. If
     // so, we can reuse the buffer and avoid client composition.
     if (mClientCompositionRequestCache) {
         if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
                                                    clientCompositionDisplay,
                                                    clientCompositionLayers)) {
+            ATRACE_NAME("ClientCompositionCacheHit");
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
             return base::unique_fd();
         }
+        ATRACE_NAME("ClientCompositionCacheMiss");
         mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
                                             clientCompositionLayers);
     }
@@ -1112,8 +1200,12 @@
     // because high frequency consumes extra battery.
     const bool expensiveBlurs =
             refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
-    const bool expensiveRenderingExpected =
-            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || expensiveBlurs;
+    const bool expensiveRenderingExpected = expensiveBlurs ||
+            std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                        [outputDataspace =
+                                 clientCompositionDisplay.outputDataspace](const auto& layer) {
+                            return layer.sourceDataspace != outputDataspace;
+                        });
     if (expensiveRenderingExpected) {
         setExpensiveRenderingExpected(true);
     }
@@ -1349,12 +1441,13 @@
     outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
 }
 
-void Output::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Output::chooseCompositionStrategy() {
     // The base output implementation can only do client composition
     auto& outputState = editState();
     outputState.usesClientComposition = true;
     outputState.usesDeviceComposition = false;
     outputState.reusedClientComposition = false;
+    return {};
 }
 
 bool Output::getSkipColorTransform() const {
@@ -1369,5 +1462,63 @@
     return result;
 }
 
+void Output::setPredictCompositionStrategy(bool predict) {
+    if (predict) {
+        mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+    } else {
+        mHwComposerAsyncWorker.reset(nullptr);
+    }
+}
+
+bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
+    if (!getState().isEnabled || !mHwComposerAsyncWorker) {
+        ALOGV("canPredictCompositionStrategy disabled");
+        return false;
+    }
+
+    if (!getState().previousDeviceRequestedChanges) {
+        ALOGV("canPredictCompositionStrategy previous changes not available");
+        return false;
+    }
+
+    if (!mRenderSurface->supportsCompositionStrategyPrediction()) {
+        ALOGV("canPredictCompositionStrategy surface does not support");
+        return false;
+    }
+
+    if (refreshArgs.devOptFlashDirtyRegionsDelay) {
+        ALOGV("canPredictCompositionStrategy devOptFlashDirtyRegionsDelay");
+        return false;
+    }
+
+    // If no layer uses clientComposition, then don't predict composition strategy
+    // because we have less work to do in parallel.
+    if (!anyLayersRequireClientComposition()) {
+        ALOGV("canPredictCompositionStrategy no layer uses clientComposition");
+        return false;
+    }
+
+    if (!refreshArgs.updatingOutputGeometryThisFrame) {
+        return true;
+    }
+
+    ALOGV("canPredictCompositionStrategy updatingOutputGeometryThisFrame");
+    return false;
+}
+
+bool Output::anyLayersRequireClientComposition() const {
+    const auto layers = getOutputLayersOrderedByZ();
+    return std::any_of(layers.begin(), layers.end(),
+                       [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+void Output::finishPrepareFrame() {
+    const auto& state = getState();
+    if (mPlanner) {
+        mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+    }
+    mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 3e983f3..723593d 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -325,7 +325,8 @@
     // For hdr content, treat the white point as the display brightness - HDR content should not be
     // boosted or dimmed.
     if (isHdrDataspace(state.dataspace) ||
-        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits) {
+        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+        getOutput().getState().displayBrightnessNits == 0.f) {
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
     } else {
@@ -506,9 +507,18 @@
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    // Don't dim cached layers
-    const auto dimmingRatio =
-            outputDependentState.overrideInfo.buffer ? 1.f : outputDependentState.dimmingRatio;
+    // Cached layers are not dimmed, which means that composer should attempt to dim.
+    // Note that if the dimming ratio is large, then this may cause the cached layer
+    // to kick back into GPU composition :(
+    // Also note that this assumes that there are no HDR layers that are able to be cached.
+    // Otherwise, this could cause HDR layers to be dimmed twice.
+    const auto dimmingRatio = outputDependentState.overrideInfo.buffer
+            ? (getOutput().getState().displayBrightnessNits != 0.f
+                       ? std::clamp(getOutput().getState().sdrWhitePointNits /
+                                            getOutput().getState().displayBrightnessNits,
+                                    0.f, 1.f)
+                       : 1.f)
+            : outputDependentState.dimmingRatio;
 
     if (auto error = hwcLayer->setBrightness(dimmingRatio); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
@@ -788,6 +798,7 @@
             }};
     settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
+    settings.whitePointNits = getOutput().getState().sdrWhitePointNits;
 
     return {static_cast<LayerFE::LayerSettings>(settings)};
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 12c2c8e..5a3af7b 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -289,5 +289,9 @@
     return mTexture;
 }
 
+bool RenderSurface::supportsCompositionStrategyPrediction() const {
+    return mDisplaySurface->supportsCompositionStrategyPrediction();
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index cd03235..36b04d9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,7 +30,9 @@
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
+
 #include <ui/Rect.h>
 #include <ui/StaticDisplayInfo.h>
 
@@ -198,6 +200,22 @@
     std::shared_ptr<Display> mDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine,
                                               getDisplayCreationArgsForPhysicalDisplay());
+
+    android::HWComposer::DeviceRequestedChanges mDeviceRequestedChanges{
+            {{nullptr, Composition::CLIENT}},
+            hal::DisplayRequest::FLIP_CLIENT_TARGET,
+            {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+            {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+            -1.f,
+    };
+
+    void chooseCompositionStrategy(Display* display) {
+        std::optional<android::HWComposer::DeviceRequestedChanges> changes =
+                display->chooseCompositionStrategy();
+        if (changes) {
+            display->applyCompositionStrategy(changes);
+        }
+    }
 };
 
 struct FullDisplayImplTestCommon : public DisplayTestCommon {
@@ -214,6 +232,11 @@
                 std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
         mDisplay->injectOutputLayerForTest(
                 std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
+        mResultWithBuffer.buffer = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
     }
 
     Layer mLayer1;
@@ -222,6 +245,8 @@
     StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
     std::shared_ptr<Display> mDisplay =
             createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
+    impl::GpuCompositionResult mResultWithBuffer;
+    impl::GpuCompositionResult mResultWithoutBuffer;
 };
 
 /*
@@ -554,7 +579,7 @@
             createPartialMockDisplay<Display>(mCompositionEngine, args);
     EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
 
-    gpuDisplay->chooseCompositionStrategy();
+    chooseCompositionStrategy(gpuDisplay.get());
 
     auto& state = gpuDisplay->getState();
     EXPECT_TRUE(state.usesClientComposition);
@@ -567,11 +592,12 @@
                 getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
-    mDisplay->chooseCompositionStrategy();
+    chooseCompositionStrategy(mDisplay.get());
 
     auto& state = mDisplay->getState();
     EXPECT_TRUE(state.usesClientComposition);
     EXPECT_FALSE(state.usesDeviceComposition);
+    EXPECT_FALSE(state.previousDeviceRequestedChanges.has_value());
 }
 
 TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
@@ -588,10 +614,16 @@
 
     EXPECT_CALL(mHwComposer,
                 getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
-            .WillOnce(Return(NO_ERROR));
+            .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+                                     Return(NO_ERROR)));
+    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+            .Times(1);
+    EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+            .Times(1);
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
-    mDisplay->chooseCompositionStrategy();
+    chooseCompositionStrategy(mDisplay.get());
 
     auto& state = mDisplay->getState();
     EXPECT_FALSE(state.usesClientComposition);
@@ -618,11 +650,17 @@
 
     EXPECT_CALL(mHwComposer,
                 getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
-            .WillOnce(Return(NO_ERROR));
+            .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+                                     Return(NO_ERROR)));
+    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+            .Times(1);
+    EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+            .Times(1);
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
     mDisplay->setNextBrightness(kDisplayBrightness);
-    mDisplay->chooseCompositionStrategy();
+    chooseCompositionStrategy(mDisplay.get());
 
     auto& state = mDisplay->getState();
     EXPECT_FALSE(state.usesClientComposition);
@@ -631,14 +669,6 @@
 }
 
 TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
-    android::HWComposer::DeviceRequestedChanges changes{
-            {{nullptr, Composition::CLIENT}},
-            hal::DisplayRequest::FLIP_CLIENT_TARGET,
-            {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
-            {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
-            -1.f,
-    };
-
     // Since two calls are made to anyLayersRequireClientComposition with different return
     // values, use a Sequence to control the matching so the values are returned in a known
     // order.
@@ -652,13 +682,15 @@
 
     EXPECT_CALL(mHwComposer,
                 getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
-            .WillOnce(DoAll(SetArgPointee<5>(changes), Return(NO_ERROR)));
-    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
-    EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
-    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+            .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
+    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+            .Times(1);
+    EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+            .Times(1);
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
-    mDisplay->chooseCompositionStrategy();
+    chooseCompositionStrategy(mDisplay.get());
 
     auto& state = mDisplay->getState();
     EXPECT_FALSE(state.usesClientComposition);
@@ -922,7 +954,7 @@
     mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    mDisplay->finishFrame({});
+    mDisplay->finishFrame({}, std::move(mResultWithBuffer));
 }
 
 TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -940,7 +972,7 @@
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    gpuDisplay->finishFrame({});
+    gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
 }
 
 TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -957,7 +989,7 @@
     gpuDisplay->editState().usesClientComposition = false;
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
-    gpuDisplay->finishFrame({});
+    gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dda0822..8eb1946 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -657,7 +657,7 @@
     EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
 }
 
-TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsAndDimmingRatioCorrectly) {
     mOutputState.sdrWhitePointNits = 200.f;
     mOutputState.displayBrightnessNits = 800.f;
 
@@ -665,12 +665,15 @@
     mLayerFEState.isColorspaceAgnostic = false;
     mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
     EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+    EXPECT_EQ(mOutputState.sdrWhitePointNits / mOutputState.displayBrightnessNits,
+              mOutputLayer.getState().dimmingRatio);
 
     mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
     mLayerFEState.isColorspaceAgnostic = false;
     mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+    EXPECT_EQ(1.f, mOutputLayer.getState().dimmingRatio);
 }
 
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
@@ -750,9 +753,10 @@
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
     static constexpr bool kLayerGenericMetadata2Mandatory = true;
     static constexpr float kWhitePointNits = 200.f;
+    static constexpr float kSdrWhitePointNits = 100.f;
     static constexpr float kDisplayBrightnessNits = 400.f;
     static constexpr float kLayerBrightness = kWhitePointNits / kDisplayBrightnessNits;
-    static constexpr float kFullLayerBrightness = 1.f;
+    static constexpr float kOverrideLayerBrightness = kSdrWhitePointNits / kDisplayBrightnessNits;
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
@@ -798,6 +802,7 @@
         mLayerFEState.acquireFence = kFence;
 
         mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+        mOutputState.sdrWhitePointNits = kSdrWhitePointNits;
 
         EXPECT_CALL(mOutput, getDisplayColorProfile())
                 .WillRepeatedly(Return(&mDisplayColorProfile));
@@ -1117,7 +1122,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kSkipAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1133,7 +1138,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kSkipAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1149,7 +1154,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1165,7 +1170,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c2521b2..af172d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -28,6 +28,7 @@
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/impl/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -989,7 +990,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD0(chooseCompositionStrategy, void());
+        MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
     };
 
     OutputPrepareFrameTest() {
@@ -1037,6 +1038,133 @@
     EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
 }
 
+struct OutputPrepareFrameAsyncTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
+        MOCK_METHOD0(updateProtectedContentState, void());
+        MOCK_METHOD2(dequeueRenderBuffer,
+                     bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
+        MOCK_METHOD0(chooseCompositionStrategyAsync,
+                     std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>());
+        MOCK_METHOD4(composeSurfaces,
+                     std::optional<base::unique_fd>(
+                             const Region&, const compositionengine::CompositionRefreshArgs&,
+                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+    };
+
+    OutputPrepareFrameAsyncTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputPrepareFrameAsyncTest, delegatesToChooseCompositionStrategyAndRenderSurface) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+    mOutput.editState().previousDeviceRequestedChanges =
+            std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+    std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+    p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
+    EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    EXPECT_TRUE(result.succeeded);
+    EXPECT_FALSE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, skipCompositionOnDequeueFailure) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+    mOutput.editState().previousDeviceRequestedChanges =
+            std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+    std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+    p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+    EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    EXPECT_FALSE(result.succeeded);
+    EXPECT_FALSE(result.bufferAvailable());
+}
+
+// Tests that in the event of hwc error when choosing composition strategy, we would fall back
+// client composition
+TEST_F(OutputPrepareFrameAsyncTest, chooseCompositionStrategyFailureCallsPrepareFrame) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+    mOutput.editState().previousDeviceRequestedChanges =
+            std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+    std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+    p.set_value({});
+    std::shared_ptr<renderengine::ExternalTexture> tex =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                      2);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+            .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+    EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    EXPECT_FALSE(result.succeeded);
+    EXPECT_TRUE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, predictionMiss) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+    mOutput.editState().previousDeviceRequestedChanges =
+            std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+    auto newDeviceRequestedChanges =
+            std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+    newDeviceRequestedChanges->clientTargetBrightness = 5.f;
+    std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+    p.set_value(newDeviceRequestedChanges);
+    std::shared_ptr<renderengine::ExternalTexture> tex =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                      2);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+            .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+    EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    EXPECT_FALSE(result.succeeded);
+    EXPECT_TRUE(result.bufferAvailable());
+}
+
 /*
  * Output::prepare()
  */
@@ -1795,10 +1923,14 @@
         MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD0(beginFrame, void());
         MOCK_METHOD0(prepareFrame, void());
+        MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
         MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
-        MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD2(finishFrame,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          GpuCompositionResult&&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
     };
 
     StrictMock<OutputPartialMock> mOutput;
@@ -1814,9 +1946,30 @@
     EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
     EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
     EXPECT_CALL(mOutput, beginFrame());
+    EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
     EXPECT_CALL(mOutput, prepareFrame());
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
-    EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+    EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
+
+    mOutput.present(args);
+}
+
+TEST_F(OutputPresentTest, predictingCompositionStrategyInvokesPrepareFrameAsync) {
+    CompositionRefreshArgs args;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+    EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+    EXPECT_CALL(mOutput, planComposition());
+    EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
+    EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+    EXPECT_CALL(mOutput, beginFrame());
+    EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
+    EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+    EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+    EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
@@ -2714,11 +2867,15 @@
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
         MOCK_METHOD(Region, getDirtyRegion, (), (const));
-        MOCK_METHOD2(composeSurfaces,
+        MOCK_METHOD4(composeSurfaces,
                      std::optional<base::unique_fd>(
-                             const Region&, const compositionengine::CompositionRefreshArgs&));
+                             const Region&, const compositionengine::CompositionRefreshArgs&,
+                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD0(prepareFrame, void());
+        MOCK_METHOD0(updateProtectedContentState, void());
+        MOCK_METHOD2(dequeueRenderBuffer,
+                     bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
     };
 
     OutputDevOptRepaintFlashTest() {
@@ -2775,7 +2932,9 @@
 
     InSequence seq;
     EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, prepareFrame());
@@ -2791,10 +2950,14 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD2(composeSurfaces,
+        MOCK_METHOD4(composeSurfaces,
                      std::optional<base::unique_fd>(
-                             const Region&, const compositionengine::CompositionRefreshArgs&));
+                             const Region&, const compositionengine::CompositionRefreshArgs&,
+                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
         MOCK_METHOD0(postFramebuffer, void());
+        MOCK_METHOD0(updateProtectedContentState, void());
+        MOCK_METHOD2(dequeueRenderBuffer,
+                     bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
     };
 
     OutputFinishFrameTest() {
@@ -2812,27 +2975,63 @@
 TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
     mOutput.mState.isEnabled = false;
 
-    mOutput.finishFrame(mRefreshArgs);
+    impl::GpuCompositionResult result;
+    mOutput.finishFrame(mRefreshArgs, std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
     mOutput.mState.isEnabled = true;
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
 
-    InSequence seq;
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _));
-
-    mOutput.finishFrame(mRefreshArgs);
+    impl::GpuCompositionResult result;
+    mOutput.finishFrame(mRefreshArgs, std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _))
+    EXPECT_CALL(mOutput, updateProtectedContentState());
+    EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
             .WillOnce(Return(ByMove(base::unique_fd())));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
 
-    mOutput.finishFrame(mRefreshArgs);
+    impl::GpuCompositionResult result;
+    mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionSucceeded) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+    impl::GpuCompositionResult result;
+    result.succeeded = true;
+    mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+
+    impl::GpuCompositionResult result;
+    result.succeeded = false;
+    result.buffer =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                      2);
+
+    EXPECT_CALL(mOutput,
+                composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+                                Eq(ByRef(result.fence))))
+            .WillOnce(Return(ByMove(base::unique_fd())));
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+    mOutput.finishFrame(mRefreshArgs, std::move(result));
 }
 
 /*
@@ -3079,8 +3278,15 @@
 
     struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
         auto execute() {
-            getInstance()->mReadyFence =
-                    getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+            base::unique_fd fence;
+            std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+            const bool success =
+                    getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
+            if (success) {
+                getInstance()->mReadyFence =
+                        getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
+                                                               externalTexture, fence);
+            }
             return nextState<FenceCheckState>();
         }
     };
@@ -3641,7 +3847,11 @@
     EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
     EXPECT_CALL(mRenderEngine, useProtectedContext(false));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
@@ -3649,7 +3859,11 @@
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
@@ -3661,7 +3875,11 @@
     EXPECT_CALL(mRenderEngine, useProtectedContext(false));
     EXPECT_CALL(*mRenderSurface, setProtected(false));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -3683,7 +3901,11 @@
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -3693,7 +3915,11 @@
     EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
@@ -3704,7 +3930,11 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
     EXPECT_CALL(mRenderEngine, useProtectedContext(true));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
@@ -3715,7 +3945,11 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
     EXPECT_CALL(*mRenderSurface, setProtected(true));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -3726,7 +3960,11 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
     EXPECT_CALL(mRenderEngine, useProtectedContext(true));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -3744,8 +3982,9 @@
 TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
     mOutput.mState.dataspace = kExpensiveOutputDataspace;
 
+    LayerFE::LayerSettings layerSettings;
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace, _))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{layerSettings}));
 
     // For this test, we also check the call order of key functions.
     InSequence seq;
@@ -3755,7 +3994,11 @@
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
 }
 
 struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
@@ -3790,7 +4033,12 @@
     mOutput.writeCompositionState(mRefreshArgs);
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
@@ -3800,7 +4048,12 @@
     mOutput.writeCompositionState(mRefreshArgs);
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
-    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+    base::unique_fd fd;
+    std::shared_ptr<renderengine::ExternalTexture> tex;
+    mOutput.updateProtectedContentState();
+    mOutput.dequeueRenderBuffer(&fd, &tex);
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 0b1c262..bd4ff13 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LayerStateTest"
 
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <compositionengine/mock/LayerFE.h>
@@ -29,7 +30,8 @@
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
-using aidl::android::hardware::graphics::composer3::Composition;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::composer3::Composition;
 
 namespace android::compositionengine::impl::planner {
 namespace {
@@ -359,6 +361,54 @@
     EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
 }
 
+TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+
+    for (uint64_t i = 0; i < 10; i++) {
+        layerFECompositionStateTwo.frameNumber = i;
+        setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                           layerFECompositionStateTwo);
+        Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+    }
+}
+
+TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer->usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER);
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+
+    for (uint64_t i = 0; i < 10; i++) {
+        setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                           layerFECompositionStateTwo);
+        Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+    }
+}
+
 TEST_F(LayerStateTest, compareBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index eef0052..610d86f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -91,6 +91,7 @@
                 static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers));
     }
 
+    mCompositionDisplay->setPredictCompositionStrategy(mFlinger->mPredictCompositionStrategy);
     mCompositionDisplay->createDisplayColorProfile(
             compositionengine::DisplayColorProfileCreationArgsBuilder()
                     .setHasWideColorGamut(args.hasWideColorGamut)
@@ -235,6 +236,15 @@
     return nullptr;
 }
 
+DisplayModePtr DisplayDevice::getModefromHwcId(uint32_t hwcId) const {
+    const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+                                 [&](DisplayModePtr mode) { return mode->getHwcId() == hwcId; });
+    if (it != mSupportedModes.end()) {
+        return *it;
+    }
+    return nullptr;
+}
+
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
     const auto physicalId = getPhysicalId();
     if (!mHwComposer.isConnected(physicalId)) {
@@ -460,7 +470,13 @@
 }
 
 ui::DisplayModeId DisplayDevice::getPreferredBootModeId() const {
-    return mCompositionDisplay->getPreferredBootModeId();
+    const auto preferredBootHwcModeId = mCompositionDisplay->getPreferredBootHwcConfigId();
+    const auto mode = getModefromHwcId(preferredBootHwcModeId);
+    if (mode == nullptr) {
+        ALOGE("%s: invalid display mode (%d)", __FUNCTION__, preferredBootHwcModeId);
+        return BAD_VALUE;
+    }
+    return mode->getId().value();
 }
 
 void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index bf6b31a..690f240 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -232,6 +232,9 @@
     // set-top boxes after a hotplug reconnect.
     DisplayModePtr getMode(DisplayModeId) const;
 
+    // Returns nullptr if the given mode ID is not supported.
+    DisplayModePtr getModefromHwcId(uint32_t) const;
+
     // Returns the refresh rate configs for this display.
     scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5b2e265..a8d439b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -269,6 +269,14 @@
                     support) = 0;
 };
 
+static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
+                              const android::HWComposer::DeviceRequestedChanges& rhs) {
+    return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
+            lhs.layerRequests == rhs.layerRequests &&
+            lhs.clientTargetProperty == rhs.clientTargetProperty &&
+            lhs.clientTargetBrightness == rhs.clientTargetBrightness;
+}
+
 namespace impl {
 
 class HWComposer final : public android::HWComposer {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 6f02843..3dab389 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -16,6 +16,8 @@
 
 //#define LOG_NDEBUG 0
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisor"
 
@@ -27,6 +29,7 @@
 #include <android-base/properties.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
+#include <utils/Trace.h>
 
 #include <android/hardware/power/1.3/IPower.h>
 #include <android/hardware/power/IPower.h>
@@ -70,6 +73,14 @@
     return timeout;
 }
 
+void traceExpensiveRendering(bool enabled) {
+    if (enabled) {
+        ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
+    } else {
+        ATRACE_ASYNC_END("ExpensiveRendering", 0);
+    }
+}
+
 } // namespace
 
 PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
@@ -247,6 +258,9 @@
     bool setExpensiveRendering(bool enabled) override {
         ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F");
         auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled);
+        if (ret.isOk()) {
+            traceExpensiveRendering(enabled);
+        }
         return ret.isOk();
     }
 
@@ -323,6 +337,9 @@
         }
 
         auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+        if (ret.isOk()) {
+            traceExpensiveRendering(enabled);
+        }
         return ret.isOk();
     }
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 307da41..e21095a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -89,6 +89,9 @@
     virtual void dumpAsString(String8& result) const;
     virtual void resizeBuffers(const ui::Size&) override;
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
+    // Virtual display surface needs to prepare the frame based on composition type. Skip
+    // any client composition prediction.
+    virtual bool supportsCompositionStrategyPrediction() const override { return false; };
 
 private:
     enum Source : size_t {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8081096..aeaf1e1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -338,6 +338,12 @@
     // Calculate effective layer transform
     mEffectiveTransform = parentTransform * getActiveTransform(s);
 
+    if (CC_UNLIKELY(!isTransformValid())) {
+        ALOGW("Stop computing bounds for %s because it has invalid transformation.",
+              getDebugName());
+        return;
+    }
+
     // Transform parent bounds to layer space
     parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
 
@@ -1326,6 +1332,10 @@
             }
         }
     }
+    if (CC_UNLIKELY(!isTransformValid())) {
+        ALOGW("Hide layer %s because it has invalid transformation.", getDebugName());
+        return true;
+    }
     return s.flags & layer_state_t::eLayerHidden;
 }
 
@@ -1858,6 +1868,11 @@
     return mEffectiveTransform;
 }
 
+bool Layer::isTransformValid() const {
+    float transformDet = getTransform().det();
+    return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
+}
+
 half Layer::getAlpha() const {
     const auto& p = mDrawingParent.promote();
 
@@ -2142,6 +2157,9 @@
             (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
         }
     }
+
+    LayerProtoHelper::writeToProto(state.destinationFrame,
+                                   [&]() { return layerInfo->mutable_destination_frame(); });
 }
 
 bool Layer::isRemovedFromCurrentState() const  {
@@ -2248,14 +2266,14 @@
 }
 
 void Layer::handleDropInputMode(gui::WindowInfo& info) const {
-    if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+    if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
         return;
     }
 
     // Check if we need to drop input unconditionally
     gui::DropInputMode dropInputMode = getDropInputMode();
     if (dropInputMode == gui::DropInputMode::ALL) {
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
         ALOGV("Dropping input for %s as requested by policy.", getDebugName());
         return;
     }
@@ -2268,7 +2286,7 @@
     // Check if the parent has set an alpha on the layer
     sp<Layer> parent = mDrawingParent.promote();
     if (parent && parent->getAlpha() != 1.0_hf) {
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
         ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
               static_cast<float>(getAlpha()));
     }
@@ -2276,7 +2294,7 @@
     // Check if the parent has cropped the buffer
     Rect bufferSize = getCroppedBufferSize(getDrawingState());
     if (!bufferSize.isValid()) {
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
         return;
     }
 
@@ -2288,7 +2306,7 @@
     bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
 
     if (croppedByParent) {
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
         ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
               getDebugName());
     } else {
@@ -2296,7 +2314,7 @@
         // input if the window is obscured. This check should be done in surfaceflinger but the
         // logic currently resides in inputflinger. So pass the if_obscured check to input to only
         // drop input events if the window is obscured.
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
     }
 }
 
@@ -2305,7 +2323,7 @@
         mDrawingState.inputInfo.name = getName();
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
         mDrawingState.inputInfo.ownerPid = mOwnerPid;
-        mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
+        mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
         mDrawingState.inputInfo.displayId = getLayerStack().id;
     }
 
@@ -2333,7 +2351,7 @@
     // If the window will be blacked out on a display because the display does not have the secure
     // flag and the layer has the secure flag set, then drop input.
     if (!displayIsSecure && isSecure()) {
-        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
     }
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 21dd5f4..2b5e337 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -752,6 +752,7 @@
                                   FrameEventHistoryDelta* outDelta);
 
     ui::Transform getTransform() const;
+    bool isTransformValid() const;
 
     // Returns the Alpha of the Surface, accounting for the Alpha
     // of parent Surfaces in the hierarchy (alpha's will be multiplied
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 43a6e55..2ece51c 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -3,4 +3,5 @@
 chaviw@google.com
 lpy@google.com
 racarr@google.com
-vishnun@google.com
+scroggo@google.com
+vishnun@google.com
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 712cd5b..f2af85e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -52,7 +52,7 @@
         return;
     }
 
-    compositor.composite(frameTime);
+    compositor.composite(frameTime, mVsyncId);
     compositor.sample();
 }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 9532e26..4082e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -35,7 +35,7 @@
 
 struct ICompositor {
     virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
-    virtual void composite(nsecs_t frameTime) = 0;
+    virtual void composite(nsecs_t frameTime, int64_t vsyncId) = 0;
     virtual void sample() = 0;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9c32b1f..f6c81c0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -169,6 +169,7 @@
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void resync() EXCLUDES(mRefreshRateConfigsLock);
+    void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. periodFlushed will be true if
     // VsyncController detected that the vsync period changed, and false otherwise.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2cd34e9..efaa975 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -480,6 +480,9 @@
     property_get("debug.sf.disable_client_composition_cache", value, "0");
     mDisableClientCompositionCache = atoi(value);
 
+    property_get("debug.sf.predict_hwc_composition_strategy", value, "0");
+    mPredictCompositionStrategy = atoi(value);
+
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
@@ -500,6 +503,8 @@
     if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
         mTransactionTracing.emplace();
     }
+
+    mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -507,7 +512,7 @@
         return LatchUnsignaledConfig::Always;
     }
 
-    if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+    if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) {
         return LatchUnsignaledConfig::AutoSingleLayer;
     }
 
@@ -1436,8 +1441,15 @@
 
 status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) {
     auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
-        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-            return getHwComposer().setBootDisplayMode(*displayId, id);
+        if (const auto displayDevice = getDisplayDeviceLocked(displayToken)) {
+            const auto mode = displayDevice->getMode(DisplayModeId{id});
+            if (mode == nullptr) {
+                ALOGE("%s: invalid display mode (%d)", __FUNCTION__, id);
+                return BAD_VALUE;
+            }
+
+            return getHwComposer().setBootDisplayMode(displayDevice->getPhysicalId(),
+                                                      mode->getHwcId());
         } else {
             ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
             return BAD_VALUE;
@@ -1957,8 +1969,8 @@
 }
 
 void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) {
-    // TODO(b/198106220): force enable HWVsync to avoid drift problem during
-    // idle.
+    ATRACE_CALL();
+    mScheduler->forceNextResync();
 }
 
 void SurfaceFlinger::setVsyncEnabled(bool enabled) {
@@ -2185,8 +2197,8 @@
     return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
-void SurfaceFlinger::composite(nsecs_t frameTime) {
-    ATRACE_CALL();
+void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) {
+    ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
     MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
     if (mPowerHintSessionData.sessionEnabled) {
         mPowerHintSessionData.compositeStart = systemTime();
@@ -2376,6 +2388,19 @@
     getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
 }
 
+bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
+    if (!isHdrDataspace(layer->getDataSpace())) {
+        return false;
+    }
+    if (mIgnoreHdrCameraLayers) {
+        auto buffer = layer->getBuffer();
+        if (buffer && (buffer->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void SurfaceFlinger::postComposition() {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -2461,7 +2486,7 @@
             mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                 const auto layerFe = layer->getCompositionEngineLayerFE();
                 if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
-                    if (isHdrDataspace(layer->getDataSpace())) {
+                    if (isHdrLayer(layer)) {
                         const auto* outputLayer =
                             compositionDisplay->getOutputLayerForLayer(layerFe);
                         if (outputLayer) {
@@ -2583,6 +2608,13 @@
     // Find the largest width and height among all the displays.
     int32_t maxDisplayWidth = 0;
     int32_t maxDisplayHeight = 0;
+
+    // If there are no displays, set a valid display bounds so we can still compute a valid layer
+    // bounds.
+    if (ON_MAIN_THREAD(mDisplays.size()) == 0) {
+        maxDisplayWidth = maxDisplayHeight = 5000;
+    }
+
     for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
         const auto& displayDevice = pair.second;
         int32_t width = displayDevice->getWidth();
@@ -3647,75 +3679,102 @@
     return false;
 }
 
+int SurfaceFlinger::flushPendingTransactionQueues(
+        std::vector<TransactionState>& transactions,
+        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
+        bool tryApplyUnsignaled) {
+    int transactionsPendingBarrier = 0;
+    auto it = mPendingTransactionQueues.begin();
+    while (it != mPendingTransactionQueues.end()) {
+        auto& [applyToken, transactionQueue] = *it;
+        while (!transactionQueue.empty()) {
+            if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
+                ATRACE_NAME("stopTransactionProcessing");
+                break;
+            }
+
+            auto& transaction = transactionQueue.front();
+            const auto ready =
+                    transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                                                  transaction.isAutoTimestamp,
+                                                  transaction.desiredPresentTime,
+                                                  transaction.originUid, transaction.states,
+                                                  bufferLayersReadyToPresent, transactions.size(),
+                                                  tryApplyUnsignaled);
+            ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+            if (ready == TransactionReadiness::NotReady) {
+                setTransactionFlags(eTransactionFlushNeeded);
+                break;
+            }
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                setTransactionFlags(eTransactionFlushNeeded);
+                break;
+            }
+            transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                const bool frameNumberChanged = 
+                    state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+                if (frameNumberChanged) {
+                    bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+                } else {
+                    // Barrier function only used for BBQ which always includes a frame number
+                    bufferLayersReadyToPresent[state.surface] =
+                        std::numeric_limits<uint64_t>::max();
+                }
+            });
+            const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
+            if (appliedUnsignaled) {
+                applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
+            }
+
+            transactions.emplace_back(std::move(transaction));
+            transactionQueue.pop();
+        }
+
+        if (transactionQueue.empty()) {
+            it = mPendingTransactionQueues.erase(it);
+            mTransactionQueueCV.broadcast();
+        } else {
+            it = std::next(it, 1);
+        }
+    }
+    return transactionsPendingBarrier;
+}
+
 bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
     std::vector<TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> bufferLayersReadyToPresent;
+    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
     std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
     {
         Mutex::Autolock _l(mStateLock);
         {
             Mutex::Autolock _l(mQueueLock);
-            // Collect transactions from pending transaction queue.
-            auto it = mPendingTransactionQueues.begin();
-            while (it != mPendingTransactionQueues.end()) {
-                auto& [applyToken, transactionQueue] = *it;
-                while (!transactionQueue.empty()) {
-                    if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
-                        ATRACE_NAME("stopTransactionProcessing");
-                        break;
-                    }
 
-                    auto& transaction = transactionQueue.front();
-                    const auto ready =
-                            transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
-                                                          transaction.isAutoTimestamp,
-                                                          transaction.desiredPresentTime,
-                                                          transaction.originUid, transaction.states,
-                                                          bufferLayersReadyToPresent,
-                                                          transactions.size());
-                    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-                    if (ready == TransactionReadiness::NotReady) {
-                        setTransactionFlags(eTransactionFlushNeeded);
-                        break;
-                    }
-                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                        bufferLayersReadyToPresent.insert(state.surface);
-                    });
-                    const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
-                    if (appliedUnsignaled) {
-                        applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
-                    }
+            int lastTransactionsPendingBarrier = 0;
+            int transactionsPendingBarrier = 0;
+            // First collect transactions from the pending transaction queues.
+            // We are not allowing unsignaled buffers here as we want to
+            // collect all the transactions from applyTokens that are ready first.
+            transactionsPendingBarrier =
+                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+                            applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false);
 
-                    transactions.emplace_back(std::move(transaction));
-                    transactionQueue.pop();
-                }
-
-                if (transactionQueue.empty()) {
-                    it = mPendingTransactionQueues.erase(it);
-                    mTransactionQueueCV.broadcast();
-                } else {
-                    it = std::next(it, 1);
-                }
-            }
-
-            // Collect transactions from current transaction queue or queue to pending transactions.
-            // Case 1: push to pending when transactionIsReadyToBeApplied is false
-            // or the first transaction was unsignaled.
-            // Case 2: push to pending when there exist a pending queue.
-            // Case 3: others are the transactions that are ready to apply.
+            // Second, collect transactions from the transaction queue.
+            // Here as well we are not allowing unsignaled buffers for the same
+            // reason as above.
             while (!mTransactionQueue.empty()) {
                 auto& transaction = mTransactionQueue.front();
                 const bool pendingTransactions =
                         mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
                 const auto ready = [&]() REQUIRES(mStateLock) {
-                    if (pendingTransactions ||
-                        stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
-                        ATRACE_NAME("pendingTransactions || stopTransactionProcessing");
+                    if (pendingTransactions) {
+                        ATRACE_NAME("pendingTransactions");
                         return TransactionReadiness::NotReady;
                     }
 
@@ -3724,25 +3783,61 @@
                                                          transaction.desiredPresentTime,
                                                          transaction.originUid, transaction.states,
                                                          bufferLayersReadyToPresent,
-                                                         transactions.size());
+                                                         transactions.size(),
+                                                         /*tryApplyUnsignaled*/ false);
                 }();
                 ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-                if (ready == TransactionReadiness::NotReady) {
+                if (ready != TransactionReadiness::Ready) {
+                    if (ready == TransactionReadiness::NotReadyBarrier) {
+                        transactionsPendingBarrier++;
+                    }
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                        bufferLayersReadyToPresent.insert(state.surface);
-                    });
-                    const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
-                    if (appliedUnsignaled) {
-                        applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
-                    }
+                        const bool frameNumberChanged = 
+                            state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+                        if (frameNumberChanged) {
+                            bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+                        } else {
+                            // Barrier function only used for BBQ which always includes a frame number.
+                            // This value only used for barrier logic.
+                            bufferLayersReadyToPresent[state.surface] =
+                                std::numeric_limits<uint64_t>::max();
+                        }
+                      });
                     transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop_front();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
             }
 
+            // Transactions with a buffer pending on a barrier may be on a different applyToken
+            // than the transaction which satisfies our barrier. In fact this is the exact use case
+            // that the primitive is designed for. This means we may first process
+            // the barrier dependent transaction, determine it ineligible to complete
+            // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+            // The barrier dependent transaction was eligible to be presented in this frame
+            // but we would have prevented it without case. To fix this we continually
+            // loop through flushPendingTransactionQueues until we perform an iteration
+            // where the number of transactionsPendingBarrier doesn't change. This way
+            // we can continue to resolve dependency chains of barriers as far as possible.
+            while (lastTransactionsPendingBarrier != transactionsPendingBarrier) {
+                lastTransactionsPendingBarrier = transactionsPendingBarrier;
+                transactionsPendingBarrier =
+                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+                        applyTokensWithUnsignaledTransactions,
+                        /*tryApplyUnsignaled*/ false);
+            }
+
+            // We collected all transactions that could apply without latching unsignaled buffers.
+            // If we are allowing latch unsignaled of some form, now it's the time to go over the
+            // transactions that were not applied and try to apply them unsignaled.
+            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
+                flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+                                              applyTokensWithUnsignaledTransactions,
+                                              /*tryApplyUnsignaled*/ true);
+            }
+
             return applyTransactions(transactions, vsyncId);
         }
     }
@@ -3849,8 +3944,9 @@
 auto SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
-        const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        size_t totalTXapplied) const -> TransactionReadiness {
+        const std::unordered_map<
+            sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
     ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
@@ -3887,7 +3983,18 @@
             continue;
         }
 
-        const bool allowLatchUnsignaled =
+        if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
+            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+            const bool willApplyBarrierFrame =
+                (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
+                (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
+            if (!willApplyBarrierFrame) {
+                ATRACE_NAME("NotReadyBarrier");
+                return TransactionReadiness::NotReadyBarrier;
+            }
+        }
+
+        const bool allowLatchUnsignaled = tryApplyUnsignaled &&
                 shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
         ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
                       allowLatchUnsignaled ? "true" : "false");
@@ -3907,8 +4014,8 @@
         if (s.hasBufferChanges()) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
             // transaction in the queue.
-            const bool hasPendingBuffer =
-                    bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
+            const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
+                bufferLayersReadyToPresent.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                 ATRACE_NAME("hasPendingBuffer");
                 return TransactionReadiness::NotReady;
@@ -3921,24 +4028,6 @@
 void SurfaceFlinger::queueTransaction(TransactionState& state) {
     Mutex::Autolock _l(mQueueLock);
 
-    // If its TransactionQueue already has a pending TransactionState or if it is pending
-    auto itr = mPendingTransactionQueues.find(state.applyToken);
-    // if this is an animation frame, wait until prior animation frame has
-    // been applied by SF
-    if (state.flags & eAnimation) {
-        while (itr != mPendingTransactionQueues.end()) {
-            status_t err =
-                    mTransactionQueueCV.waitRelative(mQueueLock, mAnimationTransactionTimeout);
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                ALOGW_IF(err == TIMED_OUT,
-                         "setTransactionState timed out "
-                         "waiting for animation frame to apply");
-                break;
-            }
-            itr = mPendingTransactionQueues.find(state.applyToken);
-        }
-    }
-
     // Generate a CountDownLatch pending state if this is a synchronous transaction.
     if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
         state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
@@ -5473,9 +5562,6 @@
         case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
         case GET_DISPLAY_DECORATION_SUPPORT:
-        // captureLayers and captureDisplay will handle the permission check in the function
-        case CAPTURE_LAYERS:
-        case CAPTURE_DISPLAY:
         case SET_FRAME_TIMELINE_INFO:
         case GET_GPU_CONTEXT_PRIORITY:
         case GET_MAX_ACQUIRED_BUFFER_COUNT: {
@@ -5510,8 +5596,7 @@
             }
             return OK;
         }
-        case ADD_TRANSACTION_TRACE_LISTENER:
-        case CAPTURE_DISPLAY_BY_ID: {
+        case ADD_TRANSACTION_TRACE_LISTENER: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int uid = ipc->getCallingUid();
             if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -5541,6 +5626,11 @@
             }
             return PERMISSION_DENIED;
         }
+        case CAPTURE_LAYERS:
+        case CAPTURE_DISPLAY:
+        case CAPTURE_DISPLAY_BY_ID:
+            LOG_FATAL("Deprecated opcode: %d", code);
+            return PERMISSION_DENIED;
     }
 
     // These codes are used for the IBinder protocol to either interrogate the recipient
@@ -6037,7 +6127,6 @@
     static bool updateOverlay =
             property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
     if (!updateOverlay) return;
-    if (Mutex::Autolock lock(mStateLock); !isRefreshRateOverlayEnabled()) return;
 
     // Update the overlay on the main thread to avoid race conditions with
     // mRefreshRateConfigs->getCurrentRefreshRate()
@@ -6047,6 +6136,7 @@
             ALOGW("%s: default display is null", __func__);
             return;
         }
+        if (!display->isRefreshRateOverlayEnabled()) return;
 
         const auto desiredActiveMode = display->getDesiredActiveMode();
         const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
@@ -7189,6 +7279,34 @@
     mLayersAdded = true;
     return true;
 }
+
+// gui::ISurfaceComposer
+binder::Status SurfaceComposerAIDL::captureDisplay(
+        const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
+    status_t status = mFlinger->captureDisplay(args, captureListener);
+    return binder::Status::fromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::captureDisplayById(
+        int64_t displayId, const sp<IScreenCaptureListener>& captureListener) {
+    status_t status;
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int uid = ipc->getCallingUid();
+    if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
+        std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
+        status = mFlinger->captureDisplay(*id, captureListener);
+    } else {
+        status = PERMISSION_DENIED;
+    }
+    return binder::Status::fromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::captureLayers(
+        const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
+    status_t status = mFlinger->captureLayers(args, captureListener);
+    return binder::Status::fromStatusT(status);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3ecce33..86c8333 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,6 +23,7 @@
  */
 
 #include <android-base/thread_annotations.h>
+#include <android/gui/BnSurfaceComposer.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <gui/BufferQueue.h>
@@ -106,9 +107,13 @@
 class RenderArea;
 class TimeStats;
 class FrameTracer;
+class ScreenCapturer;
 class WindowInfosListenerInvoker;
 
+using gui::CaptureArgs;
+using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
+using gui::LayerCaptureArgs;
 using gui::ScreenCaptureResults;
 
 namespace frametimeline {
@@ -339,6 +344,11 @@
     void disableExpensiveRendering();
     FloatRect getMaxDisplayBounds();
 
+    // If set, composition engine tries to predict the composition strategy provided by HWC
+    // based on the previous frame. If the strategy can be predicted, gpu composition will
+    // run parallel to the hwc validateDisplay call and re-run if the predition is incorrect.
+    bool mPredictCompositionStrategy = false;
+
 protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -540,9 +550,9 @@
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
             ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
 
-    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
-    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
-    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -661,7 +671,7 @@
 
     // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
     // via RenderEngine and the Composer HAL, respectively.
-    void composite(nsecs_t frameTime) override;
+    void composite(nsecs_t frameTime, int64_t vsyncId) override;
 
     // Samples the composited frame via RegionSamplingThread.
     void sample() override;
@@ -750,6 +760,12 @@
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
 
+    int flushPendingTransactionQueues(
+            std::vector<TransactionState>& transactions,
+            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
+            bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
+
     uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
                                   int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
@@ -773,14 +789,16 @@
     void commitOffscreenLayers();
     enum class TransactionReadiness {
         NotReady,
+        NotReadyBarrier,
         Ready,
         ReadyUnsignaled,
     };
     TransactionReadiness transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
-            const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            size_t totalTXapplied) const REQUIRES(mStateLock);
+            const std::unordered_map<
+                sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
+            size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock);
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
                                size_t totalTXapplied) const;
@@ -1120,6 +1138,8 @@
     void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
             REQUIRES(mStateLock);
 
+    bool isHdrLayer(Layer* layer) const;
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
     pid_t mPid;
@@ -1168,6 +1188,7 @@
     // Used to ensure we omit a callback when HDR layer info listener is newly added but the
     // scene hasn't changed
     bool mAddingHDRLayerInfoListener = false;
+    bool mIgnoreHdrCameraLayers = false;
 
     // Set during transaction application stage to track if the input info or children
     // for a layer has changed.
@@ -1401,6 +1422,22 @@
     } mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD);
 
     nsecs_t mAnimationTransactionTimeout = s2ns(5);
+
+    friend class SurfaceComposerAIDL;
+};
+
+class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
+public:
+    SurfaceComposerAIDL(sp<SurfaceFlinger> sf) { mFlinger = sf; }
+
+    binder::Status captureDisplay(const DisplayCaptureArgs&,
+                                  const sp<IScreenCaptureListener>&) override;
+    binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override;
+    binder::Status captureLayers(const LayerCaptureArgs&,
+                                 const sp<IScreenCaptureListener>&) override;
+
+private:
+    sp<SurfaceFlinger> mFlinger;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 16f6e31..20fa091 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,9 @@
     return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
 }
 
+bool ignore_hdr_camera_layers(bool defaultValue) {
+    return SurfaceFlingerProperties::ignore_hdr_camera_layers().value_or(defaultValue);
+}
+
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 8d0e426..080feee 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -100,6 +100,8 @@
 
 bool enable_sdr_dimming(bool defaultValue);
 
+bool ignore_hdr_camera_layers(bool defaultValue);
+
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/Tracing/LocklessStack.h b/services/surfaceflinger/Tracing/LocklessStack.h
new file mode 100644
index 0000000..20f2aa8
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LocklessStack.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <atomic>
+
+template <typename T>
+// Single consumer multi producer stack. We can understand the two operations independently to see
+// why they are without race condition.
+//
+// push is responsible for maintaining a linked list stored in mPush, and called from multiple
+// threads without lock. We can see that if two threads never observe the same value from
+// mPush.load, it just functions as a normal linked list. In the case where two threads observe the
+// same value, one of them has to execute the compare_exchange first. The one that doesn't execute
+// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
+// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
+// see that the process can repeat with an arbitrary number of threads.
+//
+// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
+// the entire push list with null. This is safe, since the only other reader (push)
+// of mPush will retry if it changes in between it's read and atomic compare. We
+// then store the list and pop one element.
+//
+// If we already had something in the pop list we just pop directly.
+class LocklessStack {
+public:
+    class Entry {
+    public:
+        T* mValue;
+        std::atomic<Entry*> mNext;
+        Entry(T* value): mValue(value) {}
+    };
+    std::atomic<Entry*> mPush = nullptr;
+    std::atomic<Entry*> mPop = nullptr;
+    void push(T* value) {
+        Entry* entry = new Entry(value);
+        Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
+        do {
+            entry->mNext = previousHead;
+        } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
+    }
+    T* pop() {
+        Entry* popped = mPop.load(/*std::memory_order_acquire*/);
+        if (popped) {
+            // Single consumer so this is fine
+            mPop.store(popped->mNext/* , std::memory_order_release */);
+            auto value = popped->mValue;
+            delete popped;
+            return value;
+        } else {
+            Entry *grabbedList = mPush.exchange(nullptr/* , std::memory_order_acquire */);
+            if (!grabbedList) return nullptr;
+            mPop.store(grabbedList->mNext/* , std::memory_order_release */);
+            auto value = grabbedList->mValue;
+            delete grabbedList;
+            return value;
+        }
+    }
+};
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 1e5c3e7..d249b60 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -180,13 +180,13 @@
     }
 
     if (layer.what & layer_state_t::eReparent) {
-        int32_t layerId = layer.parentSurfaceControlForChild
+        int64_t layerId = layer.parentSurfaceControlForChild
                 ? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
                 : -1;
         proto.set_parent_id(layerId);
     }
     if (layer.what & layer_state_t::eRelativeLayerChanged) {
-        int32_t layerId = layer.relativeLayerSurfaceControl
+        int64_t layerId = layer.relativeLayerSurfaceControl
                 ? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
                 : -1;
         proto.set_relative_parent_id(layerId);
@@ -342,13 +342,13 @@
     outState.merge(state);
 
     if (state.what & layer_state_t::eReparent) {
-        outState.parentId = proto.parent_id();
+        outState.parentId = static_cast<int32_t>(proto.parent_id());
     }
     if (state.what & layer_state_t::eRelativeLayerChanged) {
-        outState.relativeParentId = proto.relative_parent_id();
+        outState.relativeParentId = static_cast<int32_t>(proto.relative_parent_id());
     }
     if (state.what & layer_state_t::eInputInfoChanged) {
-        outState.inputCropId = proto.window_info_handle().crop_layer_id();
+        outState.inputCropId = static_cast<int32_t>(proto.window_info_handle().crop_layer_id());
     }
     if (state.what & layer_state_t::eBufferChanged) {
         const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
@@ -364,7 +364,7 @@
 }
 
 void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
-    layer.layerId = proto.layer_id();
+    layer.layerId = (int32_t)proto.layer_id();
     layer.what |= proto.what();
     layer.surface = mMapper->getLayerHandle(layer.layerId);
 
@@ -431,6 +431,7 @@
         layer.bufferData->frameNumber = bufferProto.frame_number();
         layer.bufferData->flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
         layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
+        layer.bufferData->acquireFence = Fence::NO_FENCE;
     }
 
     if (proto.what() & layer_state_t::eApiChanged) {
@@ -450,23 +451,25 @@
     }
 
     if (proto.what() & layer_state_t::eReparent) {
-        int32_t layerId = proto.parent_id();
+        int64_t layerId = proto.parent_id();
         if (layerId == -1) {
             layer.parentSurfaceControlForChild = nullptr;
         } else {
             layer.parentSurfaceControlForChild =
                     new SurfaceControl(SurfaceComposerClient::getDefault(),
-                                       mMapper->getLayerHandle(layerId), nullptr, layerId);
+                                       mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
+                                       nullptr, static_cast<int32_t>(layerId));
         }
     }
     if (proto.what() & layer_state_t::eRelativeLayerChanged) {
-        int32_t layerId = proto.relative_parent_id();
+        int64_t layerId = proto.relative_parent_id();
         if (layerId == -1) {
             layer.relativeLayerSurfaceControl = nullptr;
         } else {
             layer.relativeLayerSurfaceControl =
                     new SurfaceControl(SurfaceComposerClient::getDefault(),
-                                       mMapper->getLayerHandle(layerId), nullptr, layerId);
+                                       mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
+                                       nullptr, static_cast<int32_t>(layerId));
         }
         layer.z = proto.z();
     }
@@ -493,8 +496,9 @@
         inputInfo.transform.set(transformProto.tx(), transformProto.ty());
         inputInfo.replaceTouchableRegionWithCrop =
                 windowInfoProto.replace_touchable_region_with_crop();
-        int32_t layerId = windowInfoProto.crop_layer_id();
-        inputInfo.touchableRegionCropHandle = mMapper->getLayerHandle(layerId);
+        int64_t layerId = windowInfoProto.crop_layer_id();
+        inputInfo.touchableRegionCropHandle =
+                mMapper->getLayerHandle(static_cast<int32_t>(layerId));
         layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
     }
     if (proto.what() & layer_state_t::eBackgroundColorChanged) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 2f70b27..872a901 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -80,7 +80,8 @@
     public:
         virtual ~FlingerDataMapper() = default;
         virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
-        virtual int32_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
+        virtual int64_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
+        virtual int64_t getLayerId(BBinder* /* layerHandle */) const { return -1; }
         virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
         virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
         virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
@@ -106,6 +107,7 @@
     TransactionState fromProto(const proto::TransactionState&);
     void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
     void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+    std::unique_ptr<FlingerDataMapper> mMapper;
 
 private:
     proto::LayerState toProto(const layer_state_t&);
@@ -113,7 +115,6 @@
     void fromProto(const proto::LayerState&, layer_state_t& out);
     DisplayState fromProto(const proto::DisplayState&);
 
-    std::unique_ptr<FlingerDataMapper> mMapper;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index d5e837f..6381758 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -29,23 +29,16 @@
 
 namespace android {
 
-class FlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
-    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
-
+// Keeps the binder address as the layer id so we can avoid holding the tracing lock in the
+// binder thread.
+class FlatDataMapper : public TransactionProtoParser::FlingerDataMapper {
 public:
-    FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
-          : mLayerHandles(layerHandles) {}
-
-    int32_t getLayerId(const sp<IBinder>& layerHandle) const override {
+    virtual int64_t getLayerId(const sp<IBinder>& layerHandle) const {
         if (layerHandle == nullptr) {
             return -1;
         }
-        auto it = mLayerHandles.find(layerHandle->localBinder());
-        if (it == mLayerHandles.end()) {
-            ALOGW("Could not find layer handle %p", layerHandle->localBinder());
-            return -1;
-        }
-        return it->second;
+
+        return reinterpret_cast<int64_t>(layerHandle->localBinder());
     }
 
     void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
@@ -72,8 +65,33 @@
     }
 };
 
+class FlingerDataMapper : public FlatDataMapper {
+    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
+
+public:
+    FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
+          : mLayerHandles(layerHandles) {}
+
+    int64_t getLayerId(const sp<IBinder>& layerHandle) const override {
+        if (layerHandle == nullptr) {
+            return -1;
+        }
+        return getLayerId(layerHandle->localBinder());
+    }
+
+    int64_t getLayerId(BBinder* localBinder) const {
+        auto it = mLayerHandles.find(localBinder);
+        if (it == mLayerHandles.end()) {
+            ALOGW("Could not find layer handle %p", localBinder);
+            return -1;
+        }
+        return it->second;
+    }
+};
+
 TransactionTracing::TransactionTracing()
-      : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)) {
+      : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)),
+        mLockfreeProtoParser(std::make_unique<FlatDataMapper>()) {
     std::scoped_lock lock(mTraceLock);
 
     mBuffer.setSize(mBufferSizeInBytes);
@@ -129,9 +147,9 @@
 }
 
 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
-    std::scoped_lock lock(mTraceLock);
-    ATRACE_CALL();
-    mQueuedTransactions[transaction.id] = mProtoParser.toProto(transaction);
+    proto::TransactionState* state =
+            new proto::TransactionState(mLockfreeProtoParser.toProto(transaction));
+    mTransactionQueue.push(state);
 }
 
 void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
@@ -182,6 +200,38 @@
     std::scoped_lock lock(mTraceLock);
     std::vector<std::string> removedEntries;
     proto::TransactionTraceEntry entryProto;
+
+    while (auto incomingTransaction = mTransactionQueue.pop()) {
+        auto transaction = *incomingTransaction;
+        int32_t layerCount = transaction.layer_changes_size();
+        for (int i = 0; i < layerCount; i++) {
+            auto layer = transaction.mutable_layer_changes(i);
+            layer->set_layer_id(
+                mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(layer->layer_id())));
+            if ((layer->what() & layer_state_t::eReparent) && layer->parent_id() != -1) {
+                layer->set_parent_id(
+                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+                        layer->parent_id())));
+            }
+
+            if ((layer->what() & layer_state_t::eRelativeLayerChanged) &&
+                layer->relative_parent_id() != -1) {
+                layer->set_relative_parent_id(
+                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+                        layer->relative_parent_id())));
+            }
+
+            if (layer->has_window_info_handle() &&
+                layer->window_info_handle().crop_layer_id() != -1) {
+                auto input = layer->mutable_window_info_handle();
+                input->set_crop_layer_id(
+                        mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
+                            input->crop_layer_id())));
+            }
+        }
+        mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
+        delete incomingTransaction;
+    }
     for (const CommittedTransactions& entry : committedTransactions) {
         entryProto.set_elapsed_realtime_nanos(entry.timestamp);
         entryProto.set_vsync_id(entry.vsyncId);
@@ -317,9 +367,9 @@
     // Merge layer states to starting transaction state.
     for (const proto::TransactionState& transaction : removedEntry.transactions()) {
         for (const proto::LayerState& layerState : transaction.layer_changes()) {
-            auto it = mStartingStates.find(layerState.layer_id());
+            auto it = mStartingStates.find((int32_t)layerState.layer_id());
             if (it == mStartingStates.end()) {
-                ALOGW("Could not find layer id %d", layerState.layer_id());
+                ALOGW("Could not find layer id %d", (int32_t)layerState.layer_id());
                 continue;
             }
             mProtoParser.mergeFromProto(layerState, it->second);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 95256c4..4c291f9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -26,6 +26,7 @@
 #include <thread>
 
 #include "RingBuffer.h"
+#include "LocklessStack.h"
 #include "TransactionProtoParser.h"
 
 using namespace android::surfaceflinger;
@@ -78,6 +79,7 @@
     size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
     std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
             GUARDED_BY(mTraceLock);
+    LocklessStack<proto::TransactionState> mTransactionQueue;
     nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
     std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
     std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
@@ -85,6 +87,9 @@
     std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
     std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
     TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
+    // Parses the transaction to proto without holding any tracing locks so we can generate proto
+    // in the binder thread without any contention.
+    TransactionProtoParser mLockfreeProtoParser;
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
     // otherwise will push data to temporary container.
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 114f1eb..e8fe734 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_binary {
     name: "layertracegenerator",
     defaults: [
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 947fdce..cf44eff 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -204,8 +204,7 @@
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
     mFlinger.mutableMaxRenderTargetSize() = 16384;
 
-    flinger->setLayerTracingFlags(LayerTracing::TRACE_BUFFERS | LayerTracing::TRACE_INPUT |
-                                  LayerTracing::TRACE_BUFFERS);
+    flinger->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
     flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
     std::unique_ptr<TraceGenFlingerDataMapper> mapper =
             std::make_unique<TraceGenFlingerDataMapper>();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 2fcf856..b796dfe 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -271,7 +271,7 @@
 private:
     // ICompositor overrides:
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 };
 }; // namespace scheduler
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 2e9e659..3598308 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -136,6 +136,8 @@
 
   // Corner radius explicitly set on layer rather than inherited
   float requested_corner_radius = 56;
+
+  RectProto destination_frame = 57;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index fcf4499..4f99b19 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -70,7 +70,7 @@
 
 // Keep insync with layer_state_t
 message LayerState {
-    int32 layer_id = 1;
+    int64 layer_id = 1;
     // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
     // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
     // 32 bits and there's no nice way to put 64bit constants into .proto files.
@@ -161,8 +161,8 @@
     Matrix22 matrix = 11;
     float corner_radius = 12;
     uint32 background_blur_radius = 13;
-    int32 parent_id = 14;
-    int32 relative_parent_id = 15;
+    int64 parent_id = 14;
+    int64 relative_parent_id = 15;
 
     float alpha = 16;
     message Color3 {
@@ -233,7 +233,7 @@
         bool focusable = 5;
         bool has_wallpaper = 6;
         float global_scale_factor = 7;
-        int32 crop_layer_id = 8;
+        int64 crop_layer_id = 8;
         bool replace_touchable_region_with_crop = 9;
         RectProto touchable_region_crop = 10;
         Transform transform = 11;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index caeff4a..ec18054 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -152,6 +152,11 @@
     sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                    IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
 
+    // publish gui::ISurfaceComposer, the new AIDL interface
+    sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger);
+    sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
+                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
+
     startDisplayService(); // dependency on SF getting registered above
 
     if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7702ea2..bcbe21a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -461,4 +461,13 @@
     scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.enable_sdr_dimming"
+}
+
+# Ignores Camera layers when calculating HDR coverage information
+prop {
+    api_name: "ignore_hdr_camera_layers"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
 }
\ No newline at end of file
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index bf1e7e2..348a462 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -69,6 +69,10 @@
     prop_name: "ro.surface_flinger.has_wide_color_display"
   }
   prop {
+    api_name: "ignore_hdr_camera_layers"
+    prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
+  }
+  prop {
     api_name: "max_frame_buffer_acquired_buffers"
     type: Long
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index fa1a5ed..094b0ff 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -22,6 +22,8 @@
 #include <gui/LayerState.h>
 
 namespace android {
+using gui::DisplayCaptureArgs;
+using gui::LayerCaptureArgs;
 using gui::ScreenCaptureResults;
 
 namespace test {
@@ -40,11 +42,11 @@
     args.grayscale = true;
 
     Parcel p;
-    args.write(p);
+    args.writeToParcel(&p);
     p.setDataPosition(0);
 
     DisplayCaptureArgs args2;
-    args2.read(p);
+    args2.readFromParcel(&p);
 
     ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
     ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
@@ -71,11 +73,11 @@
     args.grayscale = true;
 
     Parcel p;
-    args.write(p);
+    args.writeToParcel(&p);
     p.setDataPosition(0);
 
     LayerCaptureArgs args2;
-    args2.read(p);
+    args2.readFromParcel(&p);
 
     ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
     ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
new file mode 100644
index 0000000..aa6c74e
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "transactiontrace_testsuite",
+    defaults: [
+        "libsurfaceflinger_mocks_defaults",
+        "surfaceflinger_defaults",
+        "skia_renderengine_deps",
+    ],
+    test_suites: ["device-tests"],
+    sanitize: {
+        address: false,
+    },
+    srcs: [
+        ":libsurfaceflinger_sources",
+        ":libsurfaceflinger_mock_sources",
+        ":layertracegenerator_sources",
+        "TransactionTraceTestSuite.cpp",
+    ],
+    static_libs: [
+        "libc++fs",
+    ],
+    header_libs: [
+        "libsurfaceflinger_mocks_headers",
+        "layertracegenerator_headers",
+    ],
+    data: ["testdata/*"],
+}
diff --git a/services/surfaceflinger/tests/tracing/AndroidTest.xml b/services/surfaceflinger/tests/tracing/AndroidTest.xml
new file mode 100644
index 0000000..c0aeb35
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for transactiontrace_testsuite">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push"
+        value="transactiontrace_testsuite->/data/local/tmp/transactiontrace_testsuite" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="transactiontrace_testsuite" />
+    </test>
+</configuration>
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
new file mode 100644
index 0000000..ac4354c
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <LayerTraceGenerator.h>
+#include <Tracing/TransactionProtoParser.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <log/log.h>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+class TransactionTraceTestSuite : public testing::Test,
+                                  public testing::WithParamInterface<std::filesystem::path> {
+public:
+    static std::vector<std::filesystem::path> sTransactionTraces;
+    static constexpr std::string_view sTransactionTracePrefix = "transactions_trace_";
+    static constexpr std::string_view sLayersTracePrefix = "layers_trace_";
+    static constexpr std::string_view sTracePostfix = ".winscope";
+
+    proto::TransactionTraceFile mTransactionTrace;
+    LayersTraceFileProto mExpectedLayersTraceProto;
+    LayersTraceFileProto mActualLayersTraceProto;
+
+protected:
+    void SetUp() override {
+        std::filesystem::path transactionTracePath = GetParam();
+        parseTransactionTraceFromFile(transactionTracePath.c_str(), mTransactionTrace);
+
+        std::string expectedLayersFilename = std::string(sLayersTracePrefix) +
+                transactionTracePath.filename().string().substr(sTransactionTracePrefix.length());
+        std::string expectedLayersTracePath =
+                transactionTracePath.parent_path().string() + "/" + expectedLayersFilename;
+        EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(expectedLayersTracePath)));
+        parseLayersTraceFromFile(expectedLayersTracePath.c_str(), mExpectedLayersTraceProto);
+        TemporaryDir temp_dir;
+        std::string actualLayersTracePath =
+                std::string(temp_dir.path) + "/" + expectedLayersFilename + "_actual";
+
+        EXPECT_TRUE(
+                LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str()))
+                << "Failed to generate layers trace from " << transactionTracePath;
+        EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(actualLayersTracePath)));
+        parseLayersTraceFromFile(actualLayersTracePath.c_str(), mActualLayersTraceProto);
+    }
+
+    void parseTransactionTraceFromFile(const char* transactionTracePath,
+                                       proto::TransactionTraceFile& outProto) {
+        ALOGD("Parsing file %s...", transactionTracePath);
+        std::fstream input(transactionTracePath, std::ios::in | std::ios::binary);
+        EXPECT_TRUE(input) << "Error could not open " << transactionTracePath;
+        EXPECT_TRUE(outProto.ParseFromIstream(&input))
+                << "Failed to parse " << transactionTracePath;
+    }
+
+    void parseLayersTraceFromFile(const char* layersTracePath, LayersTraceFileProto& outProto) {
+        ALOGD("Parsing file %s...", layersTracePath);
+        std::fstream input(layersTracePath, std::ios::in | std::ios::binary);
+        EXPECT_TRUE(input) << "Error could not open " << layersTracePath;
+        EXPECT_TRUE(outProto.ParseFromIstream(&input)) << "Failed to parse " << layersTracePath;
+    }
+};
+
+std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
+
+TEST_P(TransactionTraceTestSuite, validateEndState) {
+    ASSERT_GT(mActualLayersTraceProto.entry_size(), 0);
+    ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0);
+
+    auto expectedLastEntry =
+            mExpectedLayersTraceProto.entry(mExpectedLayersTraceProto.entry_size() - 1);
+    auto actualLastEntry = mActualLayersTraceProto.entry(mActualLayersTraceProto.entry_size() - 1);
+
+    EXPECT_EQ(expectedLastEntry.layers().layers_size(), actualLastEntry.layers().layers_size());
+    for (int i = 0;
+         i < expectedLastEntry.layers().layers_size() && i < actualLastEntry.layers().layers_size();
+         i++) {
+        auto expectedLayer = expectedLastEntry.layers().layers(i);
+        auto actualLayer = actualLastEntry.layers().layers(i);
+        EXPECT_EQ(expectedLayer.id(), actualLayer.id());
+        EXPECT_EQ(expectedLayer.name(), actualLayer.name());
+        EXPECT_EQ(expectedLayer.parent(), actualLayer.parent());
+        EXPECT_EQ(expectedLayer.z(), actualLayer.z());
+        EXPECT_EQ(expectedLayer.curr_frame(), actualLayer.curr_frame());
+        ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayer.name().c_str(),
+              expectedLayer.id(), expectedLayer.parent(), expectedLayer.z(),
+              expectedLayer.curr_frame());
+    }
+}
+
+std::string PrintToStringParamName(const ::testing::TestParamInfo<std::filesystem::path>& info) {
+    const auto& prefix = android::TransactionTraceTestSuite::sTransactionTracePrefix;
+    const auto& postfix = android::TransactionTraceTestSuite::sTracePostfix;
+
+    const auto& filename = info.param.filename().string();
+    return filename.substr(prefix.length(), filename.length() - prefix.length() - postfix.length());
+}
+
+INSTANTIATE_TEST_CASE_P(TransactionTraceTestSuites, TransactionTraceTestSuite,
+                        testing::ValuesIn(TransactionTraceTestSuite::sTransactionTraces),
+                        PrintToStringParamName);
+
+} // namespace android
+
+int main(int argc, char** argv) {
+    for (const auto& entry : std::filesystem::directory_iterator(
+                 android::base::GetExecutableDirectory() + "/testdata/")) {
+        if (!entry.is_regular_file()) {
+            continue;
+        }
+        const auto& filename = entry.path().filename().string();
+        const auto& prefix = android::TransactionTraceTestSuite::sTransactionTracePrefix;
+        if (filename.compare(0, prefix.length(), prefix)) {
+            continue;
+        }
+        const std::string& path = entry.path().string();
+        const auto& postfix = android::TransactionTraceTestSuite::sTracePostfix;
+        if (path.compare(path.length() - postfix.length(), postfix.length(), postfix)) {
+            continue;
+        }
+        android::TransactionTraceTestSuite::sTransactionTraces.push_back(path);
+    }
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/tracing/readme.md b/services/surfaceflinger/tests/tracing/readme.md
new file mode 100644
index 0000000..3e80a74
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/readme.md
@@ -0,0 +1,20 @@
+### TransactionTrace Testsuite ###
+
+Hassle free way to test and validate whether a sequence of
+transactions will produce the expected front end state(s). Test
+runs through all testdata/transactions_trace_*.winscope files,
+generates layer states and checks if the states match the
+corresponding layer trace in testdata.
+
+
+#### Run Test ####
+`atest transactiontrace_testsuite`
+
+
+#### Workflow ####
+Add transaction traces that resulted in front end bugs along
+with the layer trace after fixing the issue. The layer trace
+can be generated by using the layertracegenerator tool. The
+main goal of this test suite is to add regression tests with
+minimal effort.
+
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
new file mode 100644
index 0000000..9e4005c
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
new file mode 100644
index 0000000..8356ae7
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 58cd7ba..1eea023 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -89,6 +89,8 @@
         "LayerHistoryTest.cpp",
         "LayerInfoTest.cpp",
         "LayerMetadataTest.cpp",
+        "LayerTest.cpp",
+        "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
new file mode 100644
index 0000000..4974f90
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <EffectLayer.h>
+#include <gtest/gtest.h>
+#include <ui/FloatRect.h>
+#include <ui/Transform.h>
+#include <limits>
+
+#include "LayerTestUtils.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+class LayerTest : public BaseLayerTest {
+protected:
+    static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min();
+    static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max();
+    static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT};
+};
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest,
+                         testing::Values(std::make_shared<BufferStateLayerFactory>(),
+                                         std::make_shared<EffectLayerFactory>()),
+                         PrintToStringParamName);
+
+TEST_P(LayerTest, layerVisibleByDefault) {
+    sp<Layer> layer = GetParam()->createLayer(mFlinger);
+    layer->updateGeometry();
+    layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+    ASSERT_FALSE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithZeroMatrix) {
+    sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+    layer_state_t::matrix22_t matrix{0, 0, 0, 0};
+    layer->setMatrix(matrix);
+    layer->updateGeometry();
+    layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+    ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithInfMatrix) {
+    sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+    constexpr const float INF = std::numeric_limits<float>::infinity();
+    layer_state_t::matrix22_t matrix{INF, 0, 0, INF};
+    layer->setMatrix(matrix);
+    layer->updateGeometry();
+    layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+    ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+TEST_P(LayerTest, hideLayerWithNanMatrix) {
+    sp<Layer> layer = GetParam()->createLayer(mFlinger);
+
+    constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN();
+    layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN};
+    layer->setMatrix(matrix);
+    layer->updateGeometry();
+    layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
+
+    ASSERT_TRUE(layer->isHiddenByPolicy());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
new file mode 100644
index 0000000..5a2c147
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTestUtils.h"
+
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
+    sp<Client> client;
+    LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
+                           LayerMetadata());
+    return new BufferStateLayer(args);
+}
+
+sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
+    sp<Client> client;
+    LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
+    return new EffectLayer(args);
+}
+
+std::string PrintToStringParamName(
+        const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+    return info.param->name();
+}
+
+BaseLayerTest::BaseLayerTest() {
+    setupScheduler();
+}
+
+void BaseLayerTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+                            TestableSurfaceFlinger::kTwoDisplayModes);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
new file mode 100644
index 0000000..fc9b6a2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+class LayerFactory {
+public:
+    virtual ~LayerFactory() = default;
+
+    virtual std::string name() = 0;
+    virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "BufferStateLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override;
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "EffectLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override;
+};
+
+std::string PrintToStringParamName(
+        const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info);
+
+class BaseLayerTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+    BaseLayerTest();
+
+    void setupScheduler();
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 1dd7dea..e0aa0b1 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -33,7 +33,7 @@
 
 struct NoOpCompositor final : ICompositor {
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 } gNoOpCompositor;
 
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 2b69f13..825f145 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -30,73 +30,29 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 #include "FpsOps.h"
+#include "LayerTestUtils.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
 #include "mock/MockVsyncController.h"
 
 namespace android {
 
-using testing::_;
 using testing::DoAll;
 using testing::Mock;
-using testing::Return;
 using testing::SetArgPointee;
 
 using android::Hwc2::IComposer;
 using android::Hwc2::IComposerClient;
 
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
 using scheduler::LayerHistory;
 
 using FrameRate = Layer::FrameRate;
 using FrameRateCompatibility = Layer::FrameRateCompatibility;
 
-class LayerFactory {
-public:
-    virtual ~LayerFactory() = default;
-
-    virtual std::string name() = 0;
-    virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
-
-protected:
-    static constexpr uint32_t WIDTH = 100;
-    static constexpr uint32_t HEIGHT = 100;
-    static constexpr uint32_t LAYER_FLAGS = 0;
-};
-
-class BufferStateLayerFactory : public LayerFactory {
-public:
-    std::string name() override { return "BufferStateLayer"; }
-    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
-        sp<Client> client;
-        LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
-                               LayerMetadata());
-        return new BufferStateLayer(args);
-    }
-};
-
-class EffectLayerFactory : public LayerFactory {
-public:
-    std::string name() override { return "EffectLayer"; }
-    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
-        sp<Client> client;
-        LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS,
-                               LayerMetadata());
-        return new EffectLayer(args);
-    }
-};
-
-std::string PrintToStringParamName(
-        const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
-    return info.param->name();
-}
-
 /**
  * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
  */
-class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+class SetFrameRateTest : public BaseLayerTest {
 protected:
     const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
     const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
@@ -106,14 +62,10 @@
 
     SetFrameRateTest();
 
-    void setupScheduler();
-
     void addChild(sp<Layer> layer, sp<Layer> child);
     void removeChild(sp<Layer> layer, sp<Layer> child);
     void commitTransaction();
 
-    TestableSurfaceFlinger mFlinger;
-
     std::vector<sp<Layer>> mLayers;
 };
 
@@ -122,8 +74,6 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    setupScheduler();
-
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
 }
 
@@ -142,33 +92,6 @@
     }
 }
 
-void SetFrameRateTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread),
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            TestableSurfaceFlinger::kTwoDisplayModes);
-}
-
 namespace {
 
 TEST_P(SetFrameRateTest, SetAndGet) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 364d8f1..4708572 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -110,7 +110,7 @@
 private:
     // ICompositor overrides:
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 };
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d83b9bb..fe0564e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -334,16 +334,14 @@
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
-
     nsecs_t commit() {
-        constexpr int64_t kVsyncId = 123;
         const nsecs_t now = systemTime();
         const nsecs_t expectedVsyncTime = now + 10'000'000;
         mFlinger->commit(now, kVsyncId, expectedVsyncTime);
         return now;
     }
 
-    void commitAndComposite() { mFlinger->composite(commit()); }
+    void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
     auto createDisplay(const String8& displayName, bool secure) {
         return mFlinger->createDisplay(displayName, secure);
@@ -877,6 +875,8 @@
     };
 
 private:
+    constexpr static int64_t kVsyncId = 123;
+
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index eefa11f..ded7531 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -226,7 +226,7 @@
         // if this is an animation, this thread should be blocked for 5s
         // in setTransactionState waiting for transactionA to flush.  Otherwise,
         // the transaction should be placed on the pending queue
-        if (flags & (ISurfaceComposer::eAnimation | ISurfaceComposer::eSynchronous) ||
+        if (flags & (ISurfaceComposer::eSynchronous) ||
             syncInputWindows) {
             EXPECT_GE(systemTime(),
                       applicationSentTime + mFlinger.getAnimationTransactionTimeout());
@@ -288,10 +288,6 @@
     NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
 }
 
-TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Animation) {
-    NotPlacedOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
-}
-
 TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
     NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
 }
@@ -300,10 +296,6 @@
     PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
 }
 
-TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Animation) {
-    PlaceOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
-}
-
 TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
     PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
 }
@@ -551,13 +543,52 @@
                          kExpectedTransactionsPending);
 }
 
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemoveSignaledWithUnsignaledIntact) {
+TEST_F(LatchUnsignaledAutoSingleLayerTest,
+       UnsignaledNotAppliedWhenThereAreSignaled_UnsignaledFirst) {
     const sp<IBinder> kApplyToken1 =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+    const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
+    const auto kExpectedTransactionsApplied = 2u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction2 =
+            createTransactionInfo(kApplyToken3,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+
+    setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2},
+                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+    const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto signaledTransaction =
@@ -567,15 +598,23 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    const auto unsignaledTransaction =
+    const auto signaledTransaction2 =
             createTransactionInfo(kApplyToken2,
                                   {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken3,
+                                  {
                                           createComposerState(kLayerId2,
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+
+    setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
+                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index ab893a3..f5e3b77 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -81,7 +81,7 @@
         sp<IBinder> getLayerHandle(int32_t id) const override {
             return (id == 42) ? layerHandle : nullptr;
         }
-        int32_t getLayerId(const sp<IBinder>& handle) const override {
+        int64_t getLayerId(const sp<IBinder>& handle) const override {
             return (handle == layerHandle) ? 42 : -1;
         }
         sp<IBinder> getDisplayHandle(int32_t id) const {
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index cae7684..ee7e92c 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include <gui/SyncScreenCaptureListener.h>
+#include <private/gui/ComposerServiceAIDL.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 #include <functional>
@@ -31,15 +32,15 @@
 public:
     static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
                                    ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerService::getComposerService();
+        const auto sf = ComposerServiceAIDL::getComposerService();
         SurfaceComposerClient::Transaction().apply(true);
 
         captureArgs.dataspace = ui::Dataspace::V0_SRGB;
         const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        status_t status = sf->captureDisplay(captureArgs, captureListener);
+        binder::Status status = sf->captureDisplay(captureArgs, captureListener);
 
-        if (status != NO_ERROR) {
-            return status;
+        if (status.transactionError() != NO_ERROR) {
+            return status.transactionError();
         }
         captureResults = captureListener->waitForResults();
         return captureResults.result;
@@ -64,14 +65,14 @@
 
     static status_t captureLayers(LayerCaptureArgs& captureArgs,
                                   ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerService::getComposerService();
+        const auto sf = ComposerServiceAIDL::getComposerService();
         SurfaceComposerClient::Transaction().apply(true);
 
         captureArgs.dataspace = ui::Dataspace::V0_SRGB;
         const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        status_t status = sf->captureLayers(captureArgs, captureListener);
-        if (status != NO_ERROR) {
-            return status;
+        binder::Status status = sf->captureLayers(captureArgs, captureListener);
+        if (status.transactionError() != NO_ERROR) {
+            return status.transactionError();
         }
         captureResults = captureListener->waitForResults();
         return captureResults.result;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9225062..60c0cb5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -632,8 +632,10 @@
         switch (ext_bit) {
             case ProcHook::KHR_android_surface:
             case ProcHook::KHR_surface:
+            case ProcHook::KHR_surface_protected_capabilities:
             case ProcHook::EXT_swapchain_colorspace:
             case ProcHook::KHR_get_surface_capabilities2:
+            case ProcHook::GOOGLE_surfaceless_query:
                 hook_extensions_.set(ext_bit);
                 // return now as these extensions do not require HAL support
                 return;
@@ -710,8 +712,10 @@
             case ProcHook::KHR_external_fence_capabilities:
             case ProcHook::KHR_get_surface_capabilities2:
             case ProcHook::KHR_surface:
+            case ProcHook::KHR_surface_protected_capabilities:
             case ProcHook::EXT_debug_report:
             case ProcHook::EXT_swapchain_colorspace:
+            case ProcHook::GOOGLE_surfaceless_query:
             case ProcHook::ANDROID_native_buffer:
             case ProcHook::EXTENSION_CORE_1_0:
             case ProcHook::EXTENSION_CORE_1_1:
@@ -923,6 +927,9 @@
     loader_extensions.push_back({
         VK_KHR_SURFACE_EXTENSION_NAME,
         VK_KHR_SURFACE_SPEC_VERSION});
+    loader_extensions.push_back(
+        {VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME,
+         VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION});
     loader_extensions.push_back({
         VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
         VK_KHR_ANDROID_SURFACE_SPEC_VERSION});
@@ -932,6 +939,8 @@
     loader_extensions.push_back({
         VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
         VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
+    loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME,
+                                 VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION});
 
     static const VkExtensionProperties loader_debug_report_extension = {
         VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION,
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 5f37a50..b436db1 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -565,11 +565,13 @@
     if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata;
     if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace;
     if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
+    if (strcmp(name, "VK_GOOGLE_surfaceless_query") == 0) return ProcHook::GOOGLE_surfaceless_query;
     if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
     if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
     if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
     if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
+    if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
     if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
     if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 819f6b2..079f9cc 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -41,11 +41,13 @@
         EXT_hdr_metadata,
         EXT_swapchain_colorspace,
         GOOGLE_display_timing,
+        GOOGLE_surfaceless_query,
         KHR_android_surface,
         KHR_get_surface_capabilities2,
         KHR_incremental_present,
         KHR_shared_presentable_image,
         KHR_surface,
+        KHR_surface_protected_capabilities,
         KHR_swapchain,
         ANDROID_external_memory_android_hardware_buffer,
         KHR_bind_memory2,
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 4a6b4f1..0be4403 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -619,42 +619,65 @@
 
 VKAPI_ATTR
 VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
-    VkPhysicalDevice /*pdev*/,
+    VkPhysicalDevice pdev,
     VkSurfaceKHR surface,
     VkSurfaceCapabilitiesKHR* capabilities) {
     ATRACE_CALL();
 
     int err;
-    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
-
     int width, height;
-    err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-    if (err != android::OK) {
-        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
-              strerror(-err), err);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-    err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-    if (err != android::OK) {
-        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
-              strerror(-err), err);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-
     int transform_hint;
-    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != android::OK) {
-        ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
-              strerror(-err), err);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-
     int max_buffer_count;
-    err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
-    if (err != android::OK) {
-        ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
-              strerror(-err), err);
-        return VK_ERROR_SURFACE_LOST_KHR;
+    if (surface == VK_NULL_HANDLE) {
+        const InstanceData& instance_data = GetData(pdev);
+        ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+        bool surfaceless_enabled =
+            instance_data.hook_extensions.test(surfaceless);
+        if (!surfaceless_enabled) {
+            // It is an error to pass a surface==VK_NULL_HANDLE unless the
+            // VK_GOOGLE_surfaceless_query extension is enabled
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        // Support for VK_GOOGLE_surfaceless_query.  The primary purpose of this
+        // extension for this function is for
+        // VkSurfaceProtectedCapabilitiesKHR::supportsProtected.  The following
+        // four values cannot be known without a surface.  Default values will
+        // be supplied anyway, but cannot be relied upon.
+        width = 1000;
+        height = 1000;
+        transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+        max_buffer_count = 10;
+    } else {
+        ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
+
+        err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
+        if (err != android::OK) {
+            ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
+        if (err != android::OK) {
+            ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+
+        err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+                            &transform_hint);
+        if (err != android::OK) {
+            ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+
+        err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT,
+                            &max_buffer_count);
+        if (err != android::OK) {
+            ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
     }
     capabilities->minImageCount = std::min(max_buffer_count, 3);
     capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
@@ -695,23 +718,43 @@
     const InstanceData& instance_data = GetData(pdev);
 
     bool wide_color_support = false;
-    Surface& surface = *SurfaceFromHandle(surface_handle);
-    int err = native_window_get_wide_color_support(surface.window.get(),
-                                                   &wide_color_support);
-    if (err) {
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
+    uint64_t consumer_usage = 0;
     bool swapchain_ext =
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
-    ALOGV("wide_color_support is: %d", wide_color_support);
+    if (surface_handle == VK_NULL_HANDLE) {
+        ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+        bool surfaceless_enabled =
+            instance_data.hook_extensions.test(surfaceless);
+        if (!surfaceless_enabled) {
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        // Support for VK_GOOGLE_surfaceless_query.  The EGL loader
+        // unconditionally supports wide color formats, even if they will cause
+        // a SurfaceFlinger fallback.  Based on that, wide_color_support will be
+        // set to true in this case.
+        wide_color_support = true;
+
+        // TODO(b/203826952): research proper value; temporarily use the
+        // values seen on Pixel
+        consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
+    } else {
+        Surface& surface = *SurfaceFromHandle(surface_handle);
+        int err = native_window_get_wide_color_support(surface.window.get(),
+                                                       &wide_color_support);
+        if (err) {
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        ALOGV("wide_color_support is: %d", wide_color_support);
+
+        consumer_usage = surface.consumer_usage;
+    }
     wide_color_support = wide_color_support && swapchain_ext;
 
     AHardwareBuffer_Desc desc = {};
     desc.width = 1;
     desc.height = 1;
     desc.layers = 1;
-    desc.usage = surface.consumer_usage |
-                 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+    desc.usage = consumer_usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
                  AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
 
     // We must support R8G8B8A8
@@ -731,6 +774,10 @@
             VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
     }
 
+    // NOTE: Any new formats that are added must be coordinated across different
+    // Android users.  This includes the ANGLE team (a layered implementation of
+    // OpenGL-ES).
+
     desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
     if (AHardwareBuffer_isSupported(&desc)) {
         all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -770,6 +817,10 @@
                                VK_COLOR_SPACE_PASS_THROUGH_EXT});
     }
 
+    // NOTE: Any new formats that are added must be coordinated across different
+    // Android users.  This includes the ANGLE team (a layered implementation of
+    // OpenGL-ES).
+
     VkResult result = VK_SUCCESS;
     if (formats) {
         uint32_t transfer_count = all_formats.size();
@@ -814,6 +865,12 @@
                         .supportedUsageFlags;
             } break;
 
+            case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
+                VkSurfaceProtectedCapabilitiesKHR* protected_caps =
+                    reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps);
+                protected_caps->supportsProtected = VK_TRUE;
+            } break;
+
             default:
                 // Ignore all other extension structs
                 break;
@@ -865,31 +922,51 @@
 
     int err;
     int query_value;
-    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
-
-    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                        &query_value);
-    if (err != android::OK || query_value < 0) {
-        ALOGE(
-            "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
-            "value=%d",
-            strerror(-err), err, query_value);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
-
-    err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
-    if (err != android::OK || query_value < 0) {
-        ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
-              strerror(-err), err, query_value);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-    uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
-
     std::vector<VkPresentModeKHR> present_modes;
-    if (min_undequeued_buffers + 1 < max_buffer_count)
+    if (surface == VK_NULL_HANDLE) {
+        const InstanceData& instance_data = GetData(pdev);
+        ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
+        bool surfaceless_enabled =
+            instance_data.hook_extensions.test(surfaceless);
+        if (!surfaceless_enabled) {
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        // Support for VK_GOOGLE_surfaceless_query.  The primary purpose of this
+        // extension for this function is for
+        // VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and
+        // VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR.  We technically cannot
+        // know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a
+        // surface, and that cannot be relied upon.
         present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
-    present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+    } else {
+        ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
+
+        err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                            &query_value);
+        if (err != android::OK || query_value < 0) {
+            ALOGE(
+                "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
+                "value=%d",
+                strerror(-err), err, query_value);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+        err =
+            window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
+        if (err != android::OK || query_value < 0) {
+            ALOGE(
+                "NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
+                strerror(-err), err, query_value);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
+
+        if (min_undequeued_buffers + 1 < max_buffer_count)
+            present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+    }
 
     VkPhysicalDevicePresentationPropertiesANDROID present_properties;
     QueryPresentationProperties(pdev, &present_properties);
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 6a73023..af56764 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -27,11 +27,13 @@
     'VK_EXT_hdr_metadata',
     'VK_EXT_swapchain_colorspace',
     'VK_GOOGLE_display_timing',
+    'VK_GOOGLE_surfaceless_query',
     'VK_KHR_android_surface',
     'VK_KHR_get_surface_capabilities2',
     'VK_KHR_incremental_present',
     'VK_KHR_shared_presentable_image',
     'VK_KHR_surface',
+    'VK_KHR_surface_protected_capabilities',
     'VK_KHR_swapchain',
 ]