Merge "Update ANGLE namespace creation"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 28aeff4..ee32cb4 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -157,3 +157,22 @@
     ],
     static_libs: ["libgmock"],
 }
+
+
+// =======================#
+// dumpstate_test_fixture #
+// =======================#
+cc_test {
+
+    name: "dumpstate_test_fixture",
+    test_suites: ["device-tests"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+    srcs: ["tests/dumpstate_test_fixture.cpp"],
+    data: ["tests/testdata/**/*"],
+}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
deleted file mode 100644
index ea5fbf1..0000000
--- a/cmds/dumpstate/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# =======================#
-# dumpstate_test_fixture #
-# =======================#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := dumpstate_test_fixture
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := \
-       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SRC_FILES := \
-        tests/dumpstate_test_fixture.cpp
-
-LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata)
-
-include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index b13478c..bb089e6 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -18,8 +18,9 @@
 
 #include "DumpstateService.h"
 
-#include <android-base/stringprintf.h>
+#include <memory>
 
+#include <android-base/stringprintf.h>
 #include "android/os/BnDumpstate.h"
 
 #include "DumpstateInternal.h"
@@ -48,9 +49,10 @@
     return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
 }
 
+// Takes ownership of data.
 static void* callAndNotify(void* data) {
-    DumpstateInfo& ds_info = *static_cast<DumpstateInfo*>(data);
-    ds_info.ds->Run(ds_info.calling_uid, ds_info.calling_package);
+    std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
+    ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
     MYLOGD("Finished Run()\n");
     return nullptr;
 }
@@ -59,7 +61,7 @@
 
 }  // namespace
 
-DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+DumpstateService::DumpstateService() : ds_(nullptr) {
 }
 
 char const* DumpstateService::getServiceName() {
@@ -78,6 +80,8 @@
     return android::OK;
 }
 
+// Note: this method is part of the old flow and is not expected to be used in combination
+// with startBugreport.
 binder::Status DumpstateService::setListener(const std::string& name,
                                              const sp<IDumpstateListener>& listener,
                                              bool getSectionDetails,
@@ -92,20 +96,22 @@
         return binder::Status::ok();
     }
     std::lock_guard<std::mutex> lock(lock_);
-    if (ds_.listener_ != nullptr) {
-        MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+    if (ds_ == nullptr) {
+        ds_ = &(Dumpstate::GetInstance());
+    }
+    if (ds_->listener_ != nullptr) {
+        MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_->listener_name_.c_str());
         return binder::Status::ok();
     }
 
-    ds_.listener_name_ = name;
-    ds_.listener_ = listener;
-    ds_.report_section_ = getSectionDetails;
+    ds_->listener_name_ = name;
+    ds_->listener_ = listener;
+    ds_->report_section_ = getSectionDetails;
     *returned_token = new DumpstateToken();
 
     return binder::Status::ok();
 }
 
-// TODO(b/111441001): Hook up to consent service & copy final br only if user approves.
 binder::Status DumpstateService::startBugreport(int32_t calling_uid,
                                                 const std::string& calling_package,
                                                 const android::base::unique_fd& bugreport_fd,
@@ -133,21 +139,29 @@
     options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
                         screenshot_fd);
 
+    // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
+    // time.
     std::lock_guard<std::mutex> lock(lock_);
-    // TODO(b/111441001): Disallow multiple simultaneous bugreports.
-    ds_.SetOptions(std::move(options));
+    if (ds_ != nullptr) {
+        return exception(binder::Status::EX_SERVICE_SPECIFIC,
+                         "There is already a bugreport in progress");
+    }
+    ds_ = &(Dumpstate::GetInstance());
+    ds_->SetOptions(std::move(options));
     if (listener != nullptr) {
-        ds_.listener_ = listener;
+        ds_->listener_ = listener;
     }
 
-    DumpstateInfo ds_info;
-    ds_info.ds = &ds_;
-    ds_info.calling_uid = calling_uid;
-    ds_info.calling_package = calling_package;
+    DumpstateInfo* ds_info = new DumpstateInfo();
+    ds_info->ds = ds_;
+    ds_info->calling_uid = calling_uid;
+    ds_info->calling_package = calling_package;
 
     pthread_t thread;
-    status_t err = pthread_create(&thread, nullptr, callAndNotify, &ds_);
+    status_t err = pthread_create(&thread, nullptr, callAndNotify, ds_info);
     if (err != 0) {
+        delete ds_info;
+        ds_info = nullptr;
         return error(err, "Could not create a background thread.");
     }
     return binder::Status::ok();
@@ -162,32 +176,36 @@
 }
 
 status_t DumpstateService::dump(int fd, const Vector<String16>&) {
-    std::string destination = ds_.options_->bugreport_fd.get() != -1
-                                  ? StringPrintf("[fd:%d]", ds_.options_->bugreport_fd.get())
-                                  : ds_.bugreport_internal_dir_.c_str();
-    dprintf(fd, "id: %d\n", ds_.id_);
-    dprintf(fd, "pid: %d\n", ds_.pid_);
-    dprintf(fd, "update_progress: %s\n", ds_.options_->do_progress_updates ? "true" : "false");
-    dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
-    dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+    if (ds_ == nullptr) {
+        dprintf(fd, "Bugreport not in progress yet");
+        return NO_ERROR;
+    }
+    std::string destination = ds_->options_->bugreport_fd.get() != -1
+                                  ? StringPrintf("[fd:%d]", ds_->options_->bugreport_fd.get())
+                                  : ds_->bugreport_internal_dir_.c_str();
+    dprintf(fd, "id: %d\n", ds_->id_);
+    dprintf(fd, "pid: %d\n", ds_->pid_);
+    dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false");
+    dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_);
+    dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_);
     dprintf(fd, "progress:\n");
-    ds_.progress_->Dump(fd, "  ");
-    dprintf(fd, "args: %s\n", ds_.options_->args.c_str());
-    dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str());
-    dprintf(fd, "version: %s\n", ds_.version_.c_str());
+    ds_->progress_->Dump(fd, "  ");
+    dprintf(fd, "args: %s\n", ds_->options_->args.c_str());
+    dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
+    dprintf(fd, "version: %s\n", ds_->version_.c_str());
     dprintf(fd, "bugreport_dir: %s\n", destination.c_str());
-    dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
-    dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
-    dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
-    dprintf(fd, "path: %s\n", ds_.path_.c_str());
-    dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str());
-    dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
-    dprintf(fd, "name: %s\n", ds_.name_.c_str());
-    dprintf(fd, "now: %ld\n", ds_.now_);
-    dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
-    dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
-    dprintf(fd, "notification title: %s\n", ds_.options_->notification_title.c_str());
-    dprintf(fd, "notification description: %s\n", ds_.options_->notification_description.c_str());
+    dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str());
+    dprintf(fd, "log_path: %s\n", ds_->log_path_.c_str());
+    dprintf(fd, "tmp_path: %s\n", ds_->tmp_path_.c_str());
+    dprintf(fd, "path: %s\n", ds_->path_.c_str());
+    dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
+    dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str());
+    dprintf(fd, "name: %s\n", ds_->name_.c_str());
+    dprintf(fd, "now: %ld\n", ds_->now_);
+    dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false");
+    dprintf(fd, "listener: %s\n", ds_->listener_name_.c_str());
+    dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str());
+    dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str());
 
     return NO_ERROR;
 }
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index faeea53..68eda47 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -51,7 +51,11 @@
     binder::Status cancelBugreport();
 
   private:
-    Dumpstate& ds_;
+    // Dumpstate object which contains all the bugreporting logic.
+    // Note that dumpstate is a oneshot service, so this object is meant to be used at most for
+    // one bugreport.
+    // This service does not own this object.
+    Dumpstate* ds_;
     std::mutex lock_;
 };
 
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 41c42d7..49d8fe9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2481,6 +2481,13 @@
             // bugreport is not shared but made available for manual retrieval.
             return status;
         }
+        if (options_->screenshot_fd.get() != -1) {
+            bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
+                                                            options_->screenshot_fd.get());
+            if (copy_succeeded) {
+                android::os::UnlinkAndLogOnError(screenshot_path_);
+            }
+        }
     }
 
     /* vibrate a few but shortly times to let user know it's finished */
@@ -2566,9 +2573,9 @@
         return HandleUserConsentDenied();
     }
     if (consent_result == UserConsentResult::APPROVED) {
-        bool copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get());
-        if (copy_succeeded && remove(ds.path_.c_str())) {
-            MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
+        bool copy_succeeded = android::os::CopyFileToFd(path_, options_->bugreport_fd.get());
+        if (copy_succeeded) {
+            android::os::UnlinkAndLogOnError(path_);
         }
         return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
     } else if (consent_result == UserConsentResult::UNAVAILABLE) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 4766d82..7fb2f3b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -347,7 +347,6 @@
         // File descriptor to output zip file.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
-        // TODO(b/111441001): Use this fd.
         android::base::unique_fd screenshot_fd;
         // TODO: rename to MODE.
         // Extra options passed as system property.
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index c309364..488070d 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -229,3 +229,23 @@
         "libutils",
     ],
 }
+
+// OTA slot script
+cc_prebuilt_binary {
+    name: "otapreopt_slot",
+    srcs: ["otapreopt_slot.sh"],
+    init_rc: ["otapreopt.rc"],
+}
+
+// OTA postinstall script
+cc_prebuilt_binary {
+    name: "otapreopt_script",
+    srcs: ["otapreopt_script.sh"],
+    // Let this depend on otapreopt, the chroot tool and the slot script,
+    // so we just have to mention one in a configuration.
+    required: [
+        "otapreopt",
+        "otapreopt_chroot",
+        "otapreopt_slot",
+    ],
+}
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
deleted file mode 100644
index 30de0b3..0000000
--- a/cmds/installd/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# OTA slot script
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= otapreopt_slot
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := otapreopt_slot.sh
-LOCAL_INIT_RC := otapreopt.rc
-
-include $(BUILD_PREBUILT)
-
-# OTA postinstall script
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= otapreopt_script
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := otapreopt_script.sh
-
-# Let this depend on otapreopt, the chroot tool and the slot script, so we just have to mention one
-# in a configuration.
-LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot
-
-include $(BUILD_PREBUILT)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8c2bcc5..00f9f42 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -158,6 +158,15 @@
     }
 }
 
+binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
+    if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str()));
+    }
+}
+
 binder::Status checkArgumentPackageName(const std::string& packageName) {
     if (is_valid_package_name(packageName.c_str())) {
         return ok();
@@ -210,6 +219,13 @@
     }                                                       \
 }
 
+#define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) {         \
+    auto status = checkArgumentUuidTestOrNull(uuid);        \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}                                                           \
+
 #define CHECK_ARGUMENT_PACKAGE_NAME(packageName) {          \
     binder::Status status =                                 \
             checkArgumentPackageName((packageName));        \
@@ -804,25 +820,23 @@
     return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
 }
 
-// TODO(narayan): We should pass through the ceDataInode so that we can call
-// clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence
-// the copy.
 binder::Status InstalldNativeService::snapshotAppData(
         const std::unique_ptr<std::string>& volumeUuid,
-        const std::string& packageName, int32_t user, int32_t storageFlags) {
+        const std::string& packageName, int32_t user, int32_t storageFlags,
+        int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
 
-    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
-        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
-                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
-    }
-
     binder::Status res = ok();
+    // Default result to 0, it will be populated with inode of ce data snapshot
+    // if FLAG_STORAGE_CE has been passed.
+    if (_aidl_return != nullptr) *_aidl_return = 0;
+
     bool clear_ce_on_exit = false;
     bool clear_de_on_exit = false;
 
@@ -851,6 +865,24 @@
         return ok();
     }
 
+    // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set.
+    binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user,
+            storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
+    if (!clear_cache_result.isOk()) {
+        // It should be fine to continue snapshot if we for some reason failed
+        // to clear cache.
+        LOG(WARNING) << "Failed to clear cache of app " << packageName;
+    }
+
+    // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set.
+    binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user,
+            storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0);
+    if (!clear_code_cache_result.isOk()) {
+        // It should be fine to continue snapshot if we for some reason failed
+        // to clear code_cache.
+        LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
+    }
+
     if (storageFlags & FLAG_STORAGE_DE) {
         auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
         auto to = create_data_misc_de_rollback_path(volume_uuid, user);
@@ -885,6 +917,16 @@
             clear_ce_on_exit = true;
             return res;
         }
+        if (_aidl_return != nullptr) {
+            auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
+                    package_name);
+            rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
+            if (rc != 0) {
+                res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
+                clear_ce_on_exit = true;
+                return res;
+            }
+        }
     }
 
     return res;
@@ -895,17 +937,13 @@
         const int32_t appId, const int64_t ceDataInode, const std::string& seInfo,
         const int32_t user, int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
 
-    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
-        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
-                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
-    }
-
     auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
             user, package_name);
     auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
@@ -956,6 +994,39 @@
     return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
 }
 
+binder::Status InstalldNativeService::destroyAppDataSnapshot(
+        const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
+        const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+
+    if (storageFlags & FLAG_STORAGE_DE) {
+        auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
+                user, package_name);
+
+        int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
+        if (res != 0) {
+            return error(res, "Failed clearing snapshot " + de_snapshot_path);
+        }
+    }
+
+    if (storageFlags & FLAG_STORAGE_CE) {
+        auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
+                user, package_name, ceSnapshotInode);
+        int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
+        if (res != 0) {
+            return error(res, "Failed clearing snapshot " + ce_snapshot_path);
+        }
+    }
+    return ok();
+}
+
+
 binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
         const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
         const std::string& dataAppName, int32_t appId, const std::string& seInfo,
@@ -2071,8 +2142,14 @@
         return error("Failed to stat " + _pkgdir);
     }
 
+    char *con = nullptr;
+    if (lgetfilecon(pkgdir, &con) < 0) {
+        return error("Failed to lgetfilecon " + _pkgdir);
+    }
+
     if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
-        return error("Failed to chown " + _pkgdir);
+        res = error("Failed to chown " + _pkgdir);
+        goto out;
     }
 
     if (chmod(pkgdir, 0700) < 0) {
@@ -2104,7 +2181,13 @@
         goto out;
     }
 
+    if (lsetfilecon(libsymlink, con) < 0) {
+        res = error("Failed to lsetfilecon " + _libsymlink);
+        goto out;
+    }
+
 out:
+    free(con);
     if (chmod(pkgdir, s.st_mode) < 0) {
         auto msg = "Failed to cleanup chmod " + _pkgdir;
         if (res.isOk()) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 098a0c2..578132d 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -61,10 +61,14 @@
     binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
 
     binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
-            const std::string& packageName, const int32_t user, int32_t storageFlags);
+            const std::string& packageName, const int32_t user, int32_t storageFlags,
+            int64_t* _aidl_return);
     binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
             const std::string& packageName, const int32_t appId, const int64_t ceDataInode,
             const std::string& seInfo, const int32_t user, int32_t storageFlags);
+    binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
+            const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
+            int32_t storageFlags);
 
     binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
             const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 3d093b8..b345210 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -105,14 +105,12 @@
         int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
         @nullable @utf8InCpp String dexMetadata);
 
-    void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+    long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
             int userId, int storageFlags);
     void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
             int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags);
-
-    // TODO(narayan) we need an API to delete the app data snapshot as well.
-    // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid,
-    //        in @utf8InCpp String packageName, int userId, int storageFlags);
+    void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, long ceSnapshotInode, int storageFlags);
 
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 9965d58..c0f8e91 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -151,11 +151,26 @@
     //   chown root root /apex
     //   restorecon /apex
     //
+    // except we perform the `restorecon` step just after mounting the tmpfs
+    // filesystem in /postinstall/apex, so that this directory is correctly
+    // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in
+    // following operations (`chmod`, `chown`, etc.) following policies
+    // restricted to `postinstall_apex_mnt_dir`:
+    //
+    //   mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid
+    //   restorecon /postinstall/apex
+    //   chmod 0755 /postinstall/apex
+    //   chown root root /postinstall/apex
+    //
     if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
         != 0) {
         PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
         exit(209);
     }
+    if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
+        PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
+        exit(214);
+    }
     if (chmod(kPostinstallApexDir, 0755) != 0) {
         PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755";
         exit(210);
@@ -164,10 +179,6 @@
         PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
         exit(211);
     }
-    if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
-        PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
-        exit(212);
-    }
 
     // Chdir into /postinstall.
     if (chdir("/postinstall") != 0) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index d17cf5f..1d50737 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -287,8 +287,13 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // Request a snapshot of the CE content but not the DE content.
+  int64_t ce_snapshot_inode;
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_CE).isOk());
+          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
+  struct stat buf;
+  memset(&buf, 0, sizeof(buf));
+  ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf));
+  ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino);
 
   std::string ce_content, de_content;
   // At this point, we should have the CE content but not the DE content.
@@ -306,7 +311,9 @@
 
   // Request a snapshot of the DE content but not the CE content.
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+          "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode).isOk());
+  // Only DE content snapshot was requested.
+  ASSERT_EQ(ce_snapshot_inode, 0);
 
   // At this point, both the CE as well as the DE content should be fully
   // populated.
@@ -325,7 +332,7 @@
 
   // Request a snapshot of both the CE as well as the DE content.
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr).isOk());
 
   ASSERT_TRUE(android::base::ReadFileToString(
       rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
@@ -351,10 +358,13 @@
 
   auto scope_guard = android::base::make_scope_guard(deleter);
 
+  int64_t ce_snapshot_inode;
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_CE).isOk());
+          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+          "com.foo", 0, FLAG_STORAGE_DE, nullptr).isOk());
+  // No CE content snapshot was performed.
+  ASSERT_EQ(ce_snapshot_inode, 0);
 
   // The snapshot calls must succeed but there should be no snapshot
   // created.
@@ -404,7 +414,7 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr).isOk());
 
   // Previous snapshot (with data for file1) must be cleared.
   struct stat sb;
@@ -430,7 +440,62 @@
   auto scope_guard = android::base::make_scope_guard(deleter);
 
   ASSERT_FALSE(service->snapshotAppData(std::make_unique<std::string>("FOO"),
-          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+          "com.foo", 0, FLAG_STORAGE_DE, nullptr).isOk());
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+  auto fake_package_ce_cache_path = read_path_inode(fake_package_ce_path,
+      "cache", kXattrInodeCache);
+  auto fake_package_ce_code_cache_path = read_path_inode(fake_package_ce_path,
+      "code_cache", kXattrInodeCache);
+  auto fake_package_de_cache_path = fake_package_de_path + "/cache";
+  auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache";
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 700));
+
+  auto deleter = [&fake_package_ce_path, &fake_package_de_path,
+          &fake_package_ce_cache_path, &fake_package_ce_code_cache_path,
+          &fake_package_de_cache_path, &fake_package_de_code_cache_path]() {
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      delete_dir_contents(fake_package_ce_cache_path, true);
+      delete_dir_contents(fake_package_ce_code_cache_path, true);
+      delete_dir_contents(fake_package_de_cache_path, true);
+      delete_dir_contents(fake_package_de_code_cache_path, true);
+      rmdir(fake_package_ce_cache_path.c_str());
+      rmdir(fake_package_ce_code_cache_path.c_str());
+      rmdir(fake_package_de_cache_path.c_str());
+      rmdir(fake_package_de_code_cache_path.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_code_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr).isOk());
+  // The snapshot call must clear cache.
+  struct stat sb;
+  ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_ce_code_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_de_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb));
 }
 
 TEST_F(ServiceTest, RestoreAppDataSnapshot) {
@@ -486,6 +551,133 @@
   ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
 }
 
+TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) {
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+          &fake_package_ce_path, &fake_package_de_path]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  // Prepare data for snapshot.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  int64_t ce_snapshot_inode;
+  // Request a snapshot of both the CE as well as the DE content.
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
+  // Because CE data snapshot was requested, ce_snapshot_inode can't be null.
+  ASSERT_NE(0, ce_snapshot_inode);
+  // Check snapshot is there.
+  struct stat sb;
+  ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+  ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+
+
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+  // Check snapshot is deleted.
+  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) {
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+          &fake_package_ce_path, &fake_package_de_path]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  // Create a snapshot
+  ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+  // Check snapshot is deleted.
+  struct stat sb;
+  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+
+  // Check that deleting already deleted snapshot is no-op.
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+}
+
+TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) {
+  // Setup rollback data to make sure that test fails due to wrong volumeUuid
+  // being passed, not because of some other reason.
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+          &fake_package_ce_path, &fake_package_de_path]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
+          "com.foo", 0, 0, FLAG_STORAGE_DE).isOk());
+}
 
 TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) {
   // Setup rollback data to make sure that fails due to wrong volumeUuid being
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ce99fff..1782aa2 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -18,6 +18,7 @@
 #include <string.h>
 
 #include <android-base/logging.h>
+#include <android-base/scopeguard.h>
 #include <gtest/gtest.h>
 
 #include "InstalldNativeService.h"
@@ -565,6 +566,29 @@
     EXPECT_EQ("/data/misc_de/10/rollback",
             create_data_misc_de_rollback_path(nullptr, 10));
 
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0));
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239));
+
+    auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo");
+    auto deleter = [&rollback_ce_package_path]() {
+        delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */);
+    };
+    auto scope_guard = android::base::make_scope_guard(deleter);
+
+    ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700));
+
+    ino_t ce_data_inode;
+    ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode));
+
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode));
+    // Check that path defined by inode is picked even if it's not the same as
+    // the fallback one.
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode));
+
     // These last couple of cases are never exercised in production because we
     // only snapshot apps in the primary data partition. Exercise them here for
     // the sake of completeness.
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 24f5eab..5b487bb 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -70,6 +70,35 @@
     CHECK(is_valid_package_name(package_name));
 }
 
+static std::string resolve_ce_path_by_inode_or_fallback(const std::string& root_path,
+        ino_t ce_data_inode, const std::string& fallback) {
+    if (ce_data_inode != 0) {
+        DIR* dir = opendir(root_path.c_str());
+        if (dir == nullptr) {
+            PLOG(ERROR) << "Failed to opendir " << root_path;
+            return fallback;
+        }
+
+        struct dirent* ent;
+        while ((ent = readdir(dir))) {
+            if (ent->d_ino == ce_data_inode) {
+                auto resolved = StringPrintf("%s/%s", root_path.c_str(), ent->d_name);
+                if (resolved != fallback) {
+                    LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
+                            << " instead of " << fallback;
+                }
+                closedir(dir);
+                return resolved;
+            }
+        }
+        LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
+        closedir(dir);
+        return fallback;
+    } else {
+        return fallback;
+    }
+}
+
 /**
  * Create the path name where package app contents should be stored for
  * the given volume UUID and package name.  An empty UUID is assumed to
@@ -113,34 +142,8 @@
     // For testing purposes, rely on the inode when defined; this could be
     // optimized to use access() in the future.
     auto fallback = create_data_user_ce_package_path(volume_uuid, user, package_name);
-    if (ce_data_inode != 0) {
-        auto user_path = create_data_user_ce_path(volume_uuid, user);
-        DIR* dir = opendir(user_path.c_str());
-        if (dir == nullptr) {
-            PLOG(ERROR) << "Failed to opendir " << user_path;
-            return fallback;
-        }
-
-        struct dirent* ent;
-        while ((ent = readdir(dir))) {
-            if (ent->d_ino == ce_data_inode) {
-                auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
-#if DEBUG_XATTRS
-                if (resolved != fallback) {
-                    LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
-                            << " instead of " << fallback;
-                }
-#endif
-                closedir(dir);
-                return resolved;
-            }
-        }
-        LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
-        closedir(dir);
-        return fallback;
-    } else {
-        return fallback;
-    }
+    auto user_path = create_data_user_ce_path(volume_uuid, user);
+    return resolve_ce_path_by_inode_or_fallback(user_path, ce_data_inode, fallback);
 }
 
 std::string create_data_user_de_package_path(const char* volume_uuid,
@@ -209,6 +212,13 @@
            create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name);
 }
 
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name, ino_t ce_rollback_inode) {
+    auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
+    auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user);
+    return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback);
+}
+
 std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
         userid_t user, const char* package_name) {
     return StringPrintf("%s/%s",
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5afe059..0711b34 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -65,6 +65,8 @@
 std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
         userid_t user, const char* package_name);
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name, ino_t ce_rollback_inode);
 std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
         userid_t user, const char* package_name);
 
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 2b8cc57..a065a4c 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -163,6 +163,7 @@
     int32_t ownerUid;
     int32_t inputFeatures;
     int32_t displayId;
+    int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
 
     void addTouchableRegion(const Rect& region);
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 26dafd0..7b086d0 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,23 +46,24 @@
         PROCESS_STATE_PERSISTENT = 0,
         PROCESS_STATE_PERSISTENT_UI = 1,
         PROCESS_STATE_TOP = 2,
-        PROCESS_STATE_FOREGROUND_SERVICE = 3,
-        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4,
-        PROCESS_STATE_IMPORTANT_FOREGROUND = 5,
-        PROCESS_STATE_IMPORTANT_BACKGROUND = 6,
-        PROCESS_STATE_TRANSIENT_BACKGROUND = 7,
-        PROCESS_STATE_BACKUP = 8,
-        PROCESS_STATE_SERVICE = 9,
-        PROCESS_STATE_RECEIVER = 10,
-        PROCESS_STATE_TOP_SLEEPING = 11,
-        PROCESS_STATE_HEAVY_WEIGHT = 12,
-        PROCESS_STATE_HOME = 13,
-        PROCESS_STATE_LAST_ACTIVITY = 14,
-        PROCESS_STATE_CACHED_ACTIVITY = 15,
-        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16,
-        PROCESS_STATE_CACHED_RECENT = 17,
-        PROCESS_STATE_CACHED_EMPTY = 18,
-        PROCESS_STATE_NONEXISTENT = 19,
+        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
+        PROCESS_STATE_FOREGROUND_SERVICE = 4,
+        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+        PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+        PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+        PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+        PROCESS_STATE_BACKUP = 9,
+        PROCESS_STATE_SERVICE = 10,
+        PROCESS_STATE_RECEIVER = 11,
+        PROCESS_STATE_TOP_SLEEPING = 12,
+        PROCESS_STATE_HEAVY_WEIGHT = 13,
+        PROCESS_STATE_HOME = 14,
+        PROCESS_STATE_LAST_ACTIVITY = 15,
+        PROCESS_STATE_CACHED_ACTIVITY = 16,
+        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+        PROCESS_STATE_CACHED_RECENT = 18,
+        PROCESS_STATE_CACHED_EMPTY = 19,
+        PROCESS_STATE_NONEXISTENT = 20,
     };
 
     ActivityManager();
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 0826544..68011bb 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -65,12 +65,10 @@
         "include_ndk/android/*.h",
     ],
     license: "NOTICE",
-    draft: true,
 }
 
 ndk_library {
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
-    draft: true,
 }
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index aa1371f..5c5613d 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -95,6 +95,7 @@
     output.writeInt32(ownerUid);
     output.writeInt32(inputFeatures);
     output.writeInt32(displayId);
+    output.writeInt32(portalToDisplayId);
     applicationInfo.write(output);
     output.write(touchableRegion);
 
@@ -136,6 +137,7 @@
     ret.ownerUid = from.readInt32();
     ret.inputFeatures = from.readInt32();
     ret.displayId = from.readInt32();
+    ret.portalToDisplayId = from.readInt32();
     ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
 
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 5e5893f..09dd72b 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -61,6 +61,7 @@
     i.ownerUid = 24;
     i.inputFeatures = 29;
     i.displayId = 34;
+    i.portalToDisplayId = 2;
 
     Parcel p;
     i.write(p);
@@ -90,6 +91,7 @@
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
+    ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
 }
 
 } // namespace test
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 9669135..4b20772 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -167,32 +167,30 @@
         return -EINVAL;
     }
 
+    // Import fds. Dup fds because hidl_handle owns the fds.
+    unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
+    mMetadata = BufferHubMetadata::Import(std::move(ashmemFd));
+    if (!mMetadata.IsValid()) {
+        ALOGE("%s: Received an invalid metadata.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0));
+    if (!mEventFd.isValid()) {
+        ALOGE("%s: Received ad invalid event fd.", __FUNCTION__);
+        return -EINVAL;
+    }
+
     int bufferId = bufferTraits.bufferInfo->data[2];
     if (bufferId < 0) {
-        ALOGE("%s: Received an invalid (negative) id!", __FUNCTION__);
+        ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__);
         return -EINVAL;
     }
 
     uint32_t clientBitMask;
     memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
     if (clientBitMask == 0U) {
-        ALOGE("%s: Received a invalid client state mask!", __FUNCTION__);
-        return -EINVAL;
-    }
-
-    const int eventFd = bufferTraits.bufferInfo->data[1];
-    if (eventFd < 0) {
-        ALOGE("%s: Received a invalid event fd!", __FUNCTION__);
-        return -EINVAL;
-    }
-    mEventFd = BufferHubEventFd(eventFd);
-
-    // Import the metadata. Dup since hidl_handle owns the fd
-    unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
-    mMetadata = BufferHubMetadata::Import(std::move(ashmemFd));
-
-    if (!mMetadata.IsValid()) {
-        ALOGE("%s: invalid metadata.", __FUNCTION__);
+        ALOGE("%s: Received an invalid client state mask.", __FUNCTION__);
         return -EINVAL;
     }
 
@@ -320,6 +318,11 @@
     return 0;
 }
 
+bool BufferHubBuffer::IsReleased() const {
+    return (buffer_state_->load(std::memory_order_acquire) &
+            active_clients_bit_mask_->load(std::memory_order_acquire)) == 0;
+}
+
 bool BufferHubBuffer::IsValid() const {
     return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
             mEventFd.get() >= 0 && mMetadata.IsValid() && mBufferClient != nullptr;
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index 42d9320..0b6d75a 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -59,9 +59,7 @@
     const BufferHubEventFd& eventFd() const { return mEventFd; }
 
     // Returns the current value of MetadataHeader::buffer_state.
-    uint32_t buffer_state() {
-        return mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire);
-    }
+    uint32_t buffer_state() const { return buffer_state_->load(std::memory_order_acquire); }
 
     // A state mask which is unique to a buffer hub client among all its siblings sharing the same
     // concrete graphic buffer.
@@ -97,6 +95,9 @@
     // current cycle of the usage of the buffer.
     int Release();
 
+    // Returns whether the buffer is released by all active clients or not.
+    bool IsReleased() const;
+
     // Creates a token that stands for this BufferHubBuffer client and could be used for Import to
     // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
     // gralloc buffer and ashmem region for metadata. Note that the caller owns the token and
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h
index 43d900c..ff970cb 100644
--- a/libs/ui/include/ui/BufferHubDefs.h
+++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -106,11 +106,6 @@
     return high_bits == 0U;
 }
 
-// Returns true if all clients are in released state.
-static inline bool IsBufferReleased(uint32_t state) {
-    return state == 0U;
-}
-
 // Returns true if the input client is in released state.
 static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
     return (state & client_bit_mask) == 0U;
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 9e687d2..3bcd935 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "BufferHubBufferTest"
 
+#include <errno.h>
 #include <sys/epoll.h>
 
 #include <android/hardware_buffer.h>
@@ -34,7 +35,6 @@
 using ::android::BufferHubDefs::AnyClientAcquired;
 using ::android::BufferHubDefs::AnyClientGained;
 using ::android::BufferHubDefs::AnyClientPosted;
-using ::android::BufferHubDefs::IsBufferReleased;
 using ::android::BufferHubDefs::IsClientAcquired;
 using ::android::BufferHubDefs::IsClientGained;
 using ::android::BufferHubDefs::IsClientPosted;
@@ -161,19 +161,21 @@
     EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
 
     // Both buffer instances should be in released state currently.
-    EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
-    EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
+    EXPECT_TRUE(b1->IsReleased());
+    EXPECT_TRUE(b2->IsReleased());
 
     // The event fd should behave like duped event fds.
     const BufferHubEventFd& eventFd1 = b1->eventFd();
+    ASSERT_GE(eventFd1.get(), 0);
     const BufferHubEventFd& eventFd2 = b2->eventFd();
+    ASSERT_GE(eventFd2.get(), 0);
 
     base::unique_fd epollFd(epoll_create(64));
     ASSERT_GE(epollFd.get(), 0);
 
     // Add eventFd1 to epoll set, and signal eventFd2.
     epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno);
 
     std::array<epoll_event, 1> events;
     EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
@@ -227,7 +229,7 @@
 }
 
 TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
-    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_TRUE(b1->IsReleased());
 
     // Successful gaining the buffer should change the buffer state bit of b1 to
     // gained state, other client state bits to released state.
@@ -316,7 +318,7 @@
 }
 
 TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
-    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_TRUE(b1->IsReleased());
 
     // Posting from released state should fail.
     EXPECT_EQ(b1->Post(), -EBUSY);
@@ -354,7 +356,7 @@
 }
 
 TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
-    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_TRUE(b1->IsReleased());
 
     // Acquiring form released state should fail.
     EXPECT_EQ(b1->Acquire(), -EBUSY);
@@ -371,13 +373,13 @@
 }
 
 TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
-    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_TRUE(b1->IsReleased());
 
     EXPECT_EQ(b1->Release(), 0);
 }
 
 TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
-    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_TRUE(b1->IsReleased());
     ASSERT_EQ(b1->Gain(), 0);
     ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
 
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
index 11f8e57..b7f0b4b 100644
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -17,8 +17,6 @@
 #include <gtest/gtest.h>
 #include <ui/BufferHubMetadata.h>
 
-using android::BufferHubDefs::IsBufferReleased;
-
 namespace android {
 namespace dvr {
 
@@ -52,13 +50,17 @@
   BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
   EXPECT_NE(mh1, nullptr);
 
-  EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load()));
+  // Check if the newly allocated buffer is initialized in released state (i.e.
+  // state equals to 0U).
+  EXPECT_TRUE(mh1->buffer_state.load() == 0U);
 
   EXPECT_TRUE(m2.IsValid());
   BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
   EXPECT_NE(mh2, nullptr);
 
-  EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load()));
+  // Check if the newly allocated buffer is initialized in released state (i.e.
+  // state equals to 0U).
+  EXPECT_TRUE(mh2->buffer_state.load() == 0U);
 }
 
 TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index ed5a992..a47a98f 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -22,7 +22,6 @@
 using android::BufferHubDefs::AnyClientAcquired;
 using android::BufferHubDefs::AnyClientGained;
 using android::BufferHubDefs::AnyClientPosted;
-using android::BufferHubDefs::IsBufferReleased;
 using android::BufferHubDefs::IsClientAcquired;
 using android::BufferHubDefs::IsClientPosted;
 using android::BufferHubDefs::IsClientReleased;
@@ -284,7 +283,7 @@
   EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
   // Acquire and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -356,7 +355,7 @@
   // Producer state bit is in released state after post, other clients shall be
   // in posted state although there is no consumer of this buffer yet.
   ASSERT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
-  ASSERT_FALSE(IsBufferReleased(p->buffer_state()));
+  ASSERT_TRUE(p->is_released());
   ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
 
   // Gain in released state should succeed.
@@ -374,7 +373,7 @@
   for (size_t i = 0; i < kMaxConsumerCount; ++i) {
     cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
-    EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state()));
+    EXPECT_TRUE(cs[i]->is_released());
     EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
   }
 
@@ -397,12 +396,12 @@
   // All consumers have to release before the buffer is considered to be
   // released.
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
-    EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+    EXPECT_FALSE(p->is_released());
     EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
   }
 
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
   // Buffer state cross all clients must be consistent.
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
@@ -445,7 +444,7 @@
 
   // Post the gained buffer before any consumer gets created.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
   // Newly created consumer will be signalled for the posted buffer although it
@@ -481,7 +480,7 @@
   // need to wait until the releasd is confirmed before creating another
   // consumer.
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
   // Create another consumer immediately after the release, should not make the
   // buffer un-released.
@@ -489,7 +488,7 @@
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
 
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 }
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index f5761d5..bab7367 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -51,10 +51,6 @@
   return android::BufferHubDefs::IsClientAcquired(state, client_bit_mask);
 }
 
-static inline bool IsBufferReleased(uint32_t state) {
-  return android::BufferHubDefs::IsBufferReleased(state);
-}
-
 static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
   return android::BufferHubDefs::IsClientReleased(state, client_bit_mask);
 }
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
deleted file mode 100644
index 2f42ab6..0000000
--- a/opengl/libs/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
diff --git a/opengl/tests/Android.mk b/opengl/tests/Android.mk
deleted file mode 100644
index 134854a..0000000
--- a/opengl/tests/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-dirs := \
-	linetex \
-	swapinterval \
-	textures \
-	tritex \
-
-ifneq (,$(TARGET_BUILD_JAVA_SUPPORT_LEVEL))
-dirs += \
-	gl2_cameraeye \
-	gl2_java \
-	gl2_jni \
-	gldual \
-	gl_jni \
-	gl_perfapp \
-	lighting1709 \
-	testLatency \
-	testPauseResume \
-	testViewport \
-
-endif # JAVA_SUPPORT
-
-ifeq (platform,$(TARGET_BUILD_JAVA_SUPPORT_LEVEL))
-dirs += \
-	testFramerate
-
-endif # JAVA_SUPPORT platform
-
-include $(call all-named-subdir-makefiles, $(dirs))
diff --git a/opengl/tests/gl2_cameraeye/Android.bp b/opengl/tests/gl2_cameraeye/Android.bp
new file mode 100644
index 0000000..00e00df
--- /dev/null
+++ b/opengl/tests/gl2_cameraeye/Android.bp
@@ -0,0 +1,6 @@
+android_app {
+    name: "GL2CameraEye",
+    // Only compile source java files in this apk.
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_cameraeye/Android.mk b/opengl/tests/gl2_cameraeye/Android.mk
deleted file mode 100644
index 4a43a9e..0000000
--- a/opengl/tests/gl2_cameraeye/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := GL2CameraEye
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/opengl/tests/gl2_java/Android.bp b/opengl/tests/gl2_java/Android.bp
new file mode 100644
index 0000000..a8e5d7d
--- /dev/null
+++ b/opengl/tests/gl2_java/Android.bp
@@ -0,0 +1,8 @@
+//########################################################################
+// OpenGL ES 2.0 Java sample
+//########################################################################
+android_app {
+    name: "GL2Java",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_java/Android.mk b/opengl/tests/gl2_java/Android.mk
deleted file mode 100644
index 71aa5a0..0000000
--- a/opengl/tests/gl2_java/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#########################################################################
-# OpenGL ES 2.0 Java sample
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GL2Java
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp
new file mode 100644
index 0000000..65f89b1
--- /dev/null
+++ b/opengl/tests/gl2_jni/Android.bp
@@ -0,0 +1,27 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+
+android_app {
+    name: "GL2JNI",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+    jni_libs: ["libgl2jni"],
+}
+
+// Build JNI Shared Library
+cc_library_shared {
+    name: "libgl2jni",
+    cflags: [
+        "-Werror",
+        "-Wno-error=unused-parameter",
+    ],
+    srcs: ["jni/gl_code.cpp"],
+    shared_libs: [
+        "liblog",
+        "libEGL",
+        "libGLESv2",
+    ],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_jni/Android.mk b/opengl/tests/gl2_jni/Android.mk
deleted file mode 100644
index b0081c2..0000000
--- a/opengl/tests/gl2_jni/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GL2JNI
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgl2jni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
-  gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libEGL \
-	libGLESv2
-
-LOCAL_MODULE := libgl2jni
-
-LOCAL_SDK_VERSION := current
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp
new file mode 100644
index 0000000..5bec336
--- /dev/null
+++ b/opengl/tests/gl_jni/Android.bp
@@ -0,0 +1,34 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+// Build activity
+
+android_app {
+    name: "GLJNI",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+    jni_libs: ["libgljni"],
+}
+
+// Build JNI Shared Library
+
+cc_library_shared {
+    name: "libgljni",
+    cflags: [
+        "-Werror",
+        "-Wno-error=unused-parameter",
+    ],
+    srcs: ["jni/gl_code.cpp"],
+    shared_libs: [
+        "liblog",
+        "libEGL",
+        "libGLESv1_CM",
+    ],
+    sdk_version: "current",
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+}
diff --git a/opengl/tests/gl_jni/Android.mk b/opengl/tests/gl_jni/Android.mk
deleted file mode 100644
index d64dfcf..0000000
--- a/opengl/tests/gl_jni/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLJNI
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgljni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
-  gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libEGL \
-	libGLESv1_CM
-
-LOCAL_MODULE := libgljni
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_ARM_MODE := arm
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp
new file mode 100644
index 0000000..cf899ac
--- /dev/null
+++ b/opengl/tests/gl_perfapp/Android.bp
@@ -0,0 +1,27 @@
+//########################################################################
+// OpenGL ES Perf App
+// This makefile builds both an activity and a shared library.
+//########################################################################
+android_app {
+    name: "GLPerf",
+    srcs: ["**/*.java"],
+    jni_libs: ["libglperf"],
+    // Run on Eclair
+    sdk_version: "7",
+}
+
+// Build JNI Shared Library
+cc_library_shared {
+    name: "libglperf",
+    cflags: [
+        "-Werror",
+        "-Wno-error=unused-parameter",
+    ],
+    srcs: ["jni/gl_code.cpp"],
+    shared_libs: [
+        "liblog",
+        "libEGL",
+        "libGLESv2",
+    ],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/gl_perfapp/Android.mk b/opengl/tests/gl_perfapp/Android.mk
deleted file mode 100644
index 3f411ea..0000000
--- a/opengl/tests/gl_perfapp/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-#########################################################################
-# OpenGL ES Perf App
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLPerf
-
-LOCAL_JNI_SHARED_LIBRARIES := libglperf
-
-# Run on Eclair
-LOCAL_SDK_VERSION := 7
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
-  gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libEGL \
-	libGLESv2
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MODULE := libglperf
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp
new file mode 100644
index 0000000..2432566
--- /dev/null
+++ b/opengl/tests/gldual/Android.bp
@@ -0,0 +1,30 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+// Build activity
+
+android_app {
+    name: "GLDual",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+    jni_libs: ["libgldualjni"],
+}
+
+//########################################################################
+// Build JNI Shared Library
+//########################################################################
+cc_library_shared {
+    name: "libgldualjni",
+    cflags: [
+        "-Werror",
+        "-Wno-error=unused-parameter",
+    ],
+    srcs: ["jni/gl_code.cpp"],
+    shared_libs: [
+        "liblog",
+        "libEGL",
+        "libGLESv2",
+    ],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk
deleted file mode 100644
index 5bdc0a8..0000000
--- a/opengl/tests/gldual/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLDual
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgldualjni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
-  gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libEGL \
-	libGLESv2
-
-LOCAL_MODULE := libgldualjni
-
-LOCAL_SDK_VERSION := current
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/lighting1709/Android.bp b/opengl/tests/lighting1709/Android.bp
new file mode 100644
index 0000000..e734dd1
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.bp
@@ -0,0 +1,6 @@
+android_test {
+    name: "LightingTest",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+    certificate: "platform",
+}
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
deleted file mode 100644
index 0047231..0000000
--- a/opengl/tests/lighting1709/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := LightingTest
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/linetex/Android.bp b/opengl/tests/linetex/Android.bp
new file mode 100644
index 0000000..dbc2cdb
--- /dev/null
+++ b/opengl/tests/linetex/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+    name: "test-opengl-linetex",
+    srcs: ["linetex.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libEGL",
+        "libGLESv1_CM",
+        "libui",
+        "libgui",
+        "libutils",
+    ],
+    static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/linetex/Android.mk b/opengl/tests/linetex/Android.mk
deleted file mode 100644
index 3df0a0f..0000000
--- a/opengl/tests/linetex/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	linetex.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-    libEGL \
-    libGLESv1_CM \
-    libui \
-    libgui \
-    libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-linetex
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/swapinterval/Android.bp b/opengl/tests/swapinterval/Android.bp
new file mode 100644
index 0000000..eed4dff
--- /dev/null
+++ b/opengl/tests/swapinterval/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+    name: "test-opengl-swapinterval",
+    srcs: ["swapinterval.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libutils",
+        "libEGL",
+        "libGLESv1_CM",
+        "libui",
+        "libgui",
+    ],
+    static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/swapinterval/Android.mk b/opengl/tests/swapinterval/Android.mk
deleted file mode 100644
index 2a2c12f..0000000
--- a/opengl/tests/swapinterval/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	swapinterval.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-	libutils \
-    libEGL \
-    libGLESv1_CM \
-    libui \
-    libgui
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-swapinterval
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/testFramerate/Android.bp b/opengl/tests/testFramerate/Android.bp
new file mode 100644
index 0000000..5aa83b0
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.bp
@@ -0,0 +1,9 @@
+//########################################################################
+// Test framerate and look for hiccups
+//########################################################################
+
+android_app {
+    name: "TestFramerate",
+    srcs: ["**/*.java"],
+    sdk_version: "system_current",
+}
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
deleted file mode 100644
index ca6654a..0000000
--- a/opengl/tests/testFramerate/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#########################################################################
-# Test framerate and look for hiccups
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestFramerate
-LOCAL_SDK_VERSION := system_current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testLatency/Android.bp b/opengl/tests/testLatency/Android.bp
new file mode 100644
index 0000000..c516dc3
--- /dev/null
+++ b/opengl/tests/testLatency/Android.bp
@@ -0,0 +1,8 @@
+//########################################################################
+// Test end-to-end latency.
+//########################################################################
+android_app {
+    name: "TestLatency",
+    sdk_version: "8",
+    srcs: ["**/*.java"],
+}
diff --git a/opengl/tests/testLatency/Android.mk b/opengl/tests/testLatency/Android.mk
deleted file mode 100644
index 96417c7..0000000
--- a/opengl/tests/testLatency/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#########################################################################
-# Test end-to-end latency.
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SDK_VERSION := 8
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestLatency
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testPauseResume/Android.bp b/opengl/tests/testPauseResume/Android.bp
new file mode 100644
index 0000000..810e895
--- /dev/null
+++ b/opengl/tests/testPauseResume/Android.bp
@@ -0,0 +1,6 @@
+// OpenGL ES JNI sample
+android_app {
+    name: "TestEGL",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+}
diff --git a/opengl/tests/testPauseResume/Android.mk b/opengl/tests/testPauseResume/Android.mk
deleted file mode 100644
index dda5424..0000000
--- a/opengl/tests/testPauseResume/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestEGL
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testViewport/Android.bp b/opengl/tests/testViewport/Android.bp
new file mode 100644
index 0000000..629b573
--- /dev/null
+++ b/opengl/tests/testViewport/Android.bp
@@ -0,0 +1,9 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+android_app {
+    name: "TestViewport",
+    srcs: ["**/*.java"],
+    sdk_version: "8",
+}
diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
deleted file mode 100644
index 9980e7d..0000000
--- a/opengl/tests/testViewport/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestViewport
-
-# Set a specific SDK version so we can run on Froyo.
-
-LOCAL_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/textures/Android.bp b/opengl/tests/textures/Android.bp
new file mode 100644
index 0000000..84adda2
--- /dev/null
+++ b/opengl/tests/textures/Android.bp
@@ -0,0 +1,18 @@
+cc_binary {
+    name: "test-opengl-textures",
+    srcs: ["textures.cpp"],
+    shared_libs: [
+        "libcutils",
+        "libEGL",
+        "libGLESv1_CM",
+        "libui",
+        "libgui",
+        "libutils",
+    ],
+    static_libs: ["libglTest"],
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
deleted file mode 100644
index 629a2d2..0000000
--- a/opengl/tests/textures/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	textures.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-    libEGL \
-    libGLESv1_CM \
-    libui \
-    libgui \
-    libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-textures
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp
new file mode 100644
index 0000000..390397b
--- /dev/null
+++ b/opengl/tests/tritex/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+    name: "test-opengl-tritex",
+    srcs: ["tritex.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libEGL",
+        "libGLESv1_CM",
+        "libui",
+        "libgui",
+        "libutils",
+    ],
+    static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk
deleted file mode 100644
index 7055afa..0000000
--- a/opengl/tests/tritex/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	tritex.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-    libEGL \
-    libGLESv1_CM \
-    libui \
-    libgui \
-    libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-tritex
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 8133623..1f7dd41 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -93,7 +93,7 @@
  * NotifyMotionArgs::videoFrames field to drive the classification decisions.
  * The HAL is called from a separate thread.
  */
-class MotionClassifier : public MotionClassifierInterface {
+class MotionClassifier final : public MotionClassifierInterface {
 public:
     MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
     ~MotionClassifier();
@@ -201,4 +201,4 @@
 };
 
 } // namespace android
-#endif
\ No newline at end of file
+#endif
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 57181c2..9702fb2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -521,7 +521,7 @@
 }
 
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
-        int32_t x, int32_t y) {
+        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
     // Traverse windows from front to back to find touched window.
     const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     size_t numWindows = windowHandles.size();
@@ -536,10 +536,25 @@
                     bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                             | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
+                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
+                        if (portalToDisplayId != ADISPLAY_ID_NONE
+                                && portalToDisplayId != displayId) {
+                            if (addPortalWindows) {
+                                // For the monitoring channels of the display.
+                                mTempTouchState.addPortalWindow(windowHandle);
+                            }
+                            return findTouchedWindowAtLocked(
+                                    portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+                        }
                         // Found window.
                         return windowHandle;
                     }
                 }
+
+                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(
+                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+                }
             }
         }
     }
@@ -926,6 +941,22 @@
     // Add monitor channels from event's or focused display.
     addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
 
+    if (isPointerEvent) {
+        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
+        if (stateIndex >= 0) {
+            const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+            if (!state.portalWindows.isEmpty()) {
+                // The event has gone through these portal windows, so we add monitoring targets of
+                // the corresponding displays as well.
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const InputWindowInfo* windowInfo = state.portalWindows.itemAt(i)->getInfo();
+                    addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+                            -windowInfo->frameLeft, -windowInfo->frameTop);
+                }
+            }
+        }
+    }
+
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1293,37 +1324,8 @@
                 getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                 getAxisValue(AMOTION_EVENT_AXIS_Y));
-        sp<InputWindowHandle> newTouchedWindowHandle;
-        bool isTouchModal = false;
-
-        // Traverse windows from front to back to find touched window and outside targets.
-        const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
-        size_t numWindows = windowHandles.size();
-        for (size_t i = 0; i < numWindows; i++) {
-            sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
-            const InputWindowInfo* windowInfo = windowHandle->getInfo();
-            if (windowInfo->displayId != displayId) {
-                continue; // wrong display
-            }
-
-            int32_t flags = windowInfo->layoutParamsFlags;
-            if (windowInfo->visible) {
-                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
-                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                        newTouchedWindowHandle = windowHandle;
-                        break; // found touched window, exit window loop
-                    }
-                }
-
-                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(
-                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
-                }
-            }
-        }
+        sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+                displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
 
         // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr
@@ -1685,7 +1687,7 @@
 }
 
 void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
-        int32_t displayId) {
+        int32_t displayId, float xOffset, float yOffset) {
     std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
             mMonitoringChannelsByDisplay.find(displayId);
 
@@ -1698,8 +1700,8 @@
             InputTarget& target = inputTargets.editTop();
             target.inputChannel = monitoringChannels[i];
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-            target.xOffset = 0;
-            target.yOffset = 0;
+            target.xOffset = xOffset;
+            target.yOffset = yOffset;
             target.pointerIds.clear();
             target.globalScaleFactor = 1.0f;
         }
@@ -3102,7 +3104,8 @@
             Vector<sp<InputWindowHandle>> newHandles;
             for (size_t i = 0; i < numWindows; i++) {
                 const sp<InputWindowHandle>& handle = inputWindowHandles.itemAt(i);
-                if (!handle->updateInfo() || getInputChannelLocked(handle->getToken()) == nullptr) {
+                if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr
+                        && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) {
                     ALOGE("Window handle %s has no registered input channel",
                             handle->getName().c_str());
                     continue;
@@ -3537,6 +3540,14 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
+            if (!state.portalWindows.isEmpty()) {
+                dump += INDENT3 "Portal windows:\n";
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const sp<InputWindowHandle> portalWindowHandle = state.portalWindows.itemAt(i);
+                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
+                            i, portalWindowHandle->getName().c_str());
+                }
+            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
@@ -3553,11 +3564,12 @@
                     const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
-                            "paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                            "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
                             "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                             "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
                             "touchableRegion=",
                             i, windowInfo->name.c_str(), windowInfo->displayId,
+                            windowInfo->portalToDisplayId,
                             toString(windowInfo->paused),
                             toString(windowInfo->hasFocus),
                             toString(windowInfo->hasWallpaper),
@@ -4906,6 +4918,7 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
+    portalWindows.clear();
 }
 
 void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4915,6 +4928,7 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
+    portalWindows = other.portalWindows;
 }
 
 void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4943,6 +4957,17 @@
     touchedWindow.pointerIds = pointerIds;
 }
 
+void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+    size_t numWindows = portalWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        sp<InputWindowHandle> portalWindowHandle = portalWindows.itemAt(i);
+        if (portalWindowHandle == windowHandle) {
+            return;
+        }
+    }
+    portalWindows.push_back(windowHandle);
+}
+
 void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
     for (size_t i = 0; i < windows.size(); i++) {
         if (windows.itemAt(i).windowHandle == windowHandle) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 327dbbd..2d8df5c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -921,7 +921,8 @@
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent;
 
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
+    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+            bool addOutsideTargets = false, bool addPortalWindows = false);
 
     // All registered connections mapped by channel file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByFd;
@@ -1016,12 +1017,18 @@
         int32_t displayId; // id to the display that currently has a touch, others are rejected
         Vector<TouchedWindow> windows;
 
+        // This collects the portal windows that the touch has gone through. Each portal window
+        // targets a display (embedded display for most cases). With this info, we can add the
+        // monitoring channels of the displays touched.
+        Vector<sp<InputWindowHandle>> portalWindows;
+
         TouchState();
         ~TouchState();
         void reset();
         void copyFrom(const TouchState& other);
         void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
                 int32_t targetFlags, BitSet32 pointerIds);
+        void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
         void removeWindow(const sp<InputWindowHandle>& windowHandle);
         void removeWindowByToken(const sp<IBinder>& token);
         void filterNonAsIsTouchWindows();
@@ -1096,7 +1103,8 @@
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
-    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId);
+    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId,
+            float xOffset = 0, float yOffset = 0);
 
     void pokeUserActivityLocked(const EventEntry* eventEntry);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 64070e3..f84aa34 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -805,6 +805,30 @@
     return false;
 }
 
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+        return false;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+    // No associated display. By default, can dispatch to all displays.
+    if (!associatedDisplayId) {
+        return true;
+    }
+
+    if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+        ALOGW("Device has associated, but no associated display id.");
+        return true;
+    }
+
+    return *associatedDisplayId == displayId;
+}
+
 void InputReader::dump(std::string& dump) {
     AutoMutex _l(mLock);
 
@@ -1275,6 +1299,18 @@
     mContext->getListener()->notifyDeviceReset(&args);
 }
 
+std::optional<int32_t> InputDevice::getAssociatedDisplay() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
+        if (associatedDisplayId) {
+            return associatedDisplayId;
+        }
+    }
+
+    return std::nullopt;
+}
 
 // --- CursorButtonAccumulator ---
 
@@ -2647,7 +2683,7 @@
         }
 
         // Update the PointerController if viewports changed.
-        if (mParameters.hasAssociatedDisplay) {
+        if (mParameters.mode == Parameters::MODE_POINTER) {
             getPolicy()->obtainPointerController(getDeviceId());
         }
         bumpGeneration();
@@ -2919,6 +2955,19 @@
     }
 }
 
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mParameters.mode == Parameters::MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            // If the device is orientationAware and not a mouse,
+            // it expects to dispatch events to any display
+            return std::make_optional(ADISPLAY_ID_NONE);
+        }
+    }
+    return std::nullopt;
+}
+
 // --- RotaryEncoderInputMapper ---
 
 RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
@@ -6511,9 +6560,7 @@
             ALOG_ASSERT(false);
         }
     }
-    const int32_t displayId = mPointerController != nullptr ?
-            mPointerController->getDisplayId() : mViewport.displayId;
-
+    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
     NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
@@ -6830,6 +6877,16 @@
     return true;
 }
 
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            return std::make_optional(mViewport.displayId);
+        }
+    }
+    return std::nullopt;
+}
 
 // --- SingleTouchInputMapper ---
 
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index aaffce2..fed55ac 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -146,6 +146,7 @@
             ssize_t repeat, int32_t token);
     virtual void cancelVibrate(int32_t deviceId, int32_t token);
 
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
@@ -320,6 +321,7 @@
         return value;
     }
 
+    std::optional<int32_t> getAssociatedDisplay();
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -778,7 +780,9 @@
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual void fadePointer();
-
+    virtual std::optional<int32_t> getAssociatedDisplay() {
+        return std::nullopt;
+    }
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
@@ -932,6 +936,7 @@
 
     virtual void fadePointer();
 
+    virtual std::optional<int32_t> getAssociatedDisplay();
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -1025,7 +1030,7 @@
     virtual void cancelTouch(nsecs_t when);
     virtual void timeoutExpired(nsecs_t when);
     virtual void updateExternalStylusState(const StylusState& state);
-
+    virtual std::optional<int32_t> getAssociatedDisplay();
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
     CursorScrollAccumulator mCursorScrollAccumulator;
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
new file mode 100644
index 0000000..0313a40
--- /dev/null
+++ b/services/inputflinger/OWNERS
@@ -0,0 +1,2 @@
+michaelwr@google.com
+svv@google.com
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fe1c50b..c411ec0 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -100,6 +100,9 @@
     virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
             ssize_t repeat, int32_t token) = 0;
     virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+    /* Return true if the device can send input events to the specified display. */
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
 
 /* Reads raw events from the event hub and processes them, endlessly. */
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 0b86555..84c5ad6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -841,6 +841,7 @@
     bool mResetWasCalled;
     bool mProcessWasCalled;
 
+    std::optional<DisplayViewport> mViewport;
 public:
     FakeInputMapper(InputDevice* device, uint32_t sources) :
             InputMapper(device),
@@ -909,8 +910,14 @@
         }
     }
 
-    virtual void configure(nsecs_t, const InputReaderConfiguration*, uint32_t) {
+    virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
         mConfigureWasCalled = true;
+
+        // Find the associated viewport if exist.
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mViewport = config->getDisplayViewportByPort(*displayPort);
+        }
     }
 
     virtual void reset(nsecs_t) {
@@ -957,6 +964,13 @@
 
     virtual void fadePointer() {
     }
+
+    virtual std::optional<int32_t> getAssociatedDisplay() {
+        if (mViewport) {
+            return std::make_optional(mViewport->displayId);
+        }
+        return std::nullopt;
+    }
 };
 
 
@@ -984,9 +998,10 @@
     }
 
     InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
-            uint32_t classes) {
+            uint32_t classes, const std::string& location = "") {
         InputDeviceIdentifier identifier;
         identifier.name = name;
+        identifier.location = location;
         int32_t generation = deviceId + 1;
         return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
                 classes);
@@ -1286,7 +1301,7 @@
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
     constexpr int32_t deviceId = 1;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0, "fake", deviceClass);
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
     // Must add at least one mapper or the device will be ignored!
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
     device->addMapper(mapper);
@@ -1470,7 +1485,7 @@
 TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
     constexpr int32_t deviceId = 1;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0, "fake", deviceClass);
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
     // Must add at least one mapper or the device will be ignored!
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
     device->addMapper(mapper);
@@ -1500,6 +1515,36 @@
     prevSequenceNum = resetArgs.sequenceNum;
 }
 
+TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
+    constexpr int32_t deviceId = 1;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    const char* DEVICE_LOCATION = "USB1";
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
+            DEVICE_LOCATION);
+    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
+    device->addMapper(mapper);
+    mReader->setNextDevice(device);
+    addDevice(deviceId, "fake", deviceClass, nullptr);
+
+    const uint8_t hdmi1 = 1;
+
+    // Associated touch screen with second display.
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+
+    // Add default and second display.
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    mReader->loopOnce();
+
+    // Check device.
+    ASSERT_EQ(deviceId, device->getId());
+    ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
+    ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+}
+
 
 // --- InputDeviceTest ---
 
diff --git a/services/nativeperms/Android.bp b/services/nativeperms/Android.bp
new file mode 100644
index 0000000..cbc7d66
--- /dev/null
+++ b/services/nativeperms/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2016 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.
+//
+
+cc_binary {
+    name: "nativeperms",
+    srcs: [
+        "nativeperms.cpp",
+        "android/os/IPermissionController.aidl",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libbrillo",
+        "libbrillo-binder",
+        "libchrome",
+        "libutils",
+    ],
+    init_rc: ["nativeperms.rc"],
+}
diff --git a/services/nativeperms/Android.mk b/services/nativeperms/Android.mk
deleted file mode 100644
index 34ccd0b..0000000
--- a/services/nativeperms/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := nativeperms
-LOCAL_SRC_FILES := \
-    nativeperms.cpp \
-    android/os/IPermissionController.aidl
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    libbrillo \
-    libbrillo-binder \
-    libchrome \
-    libutils
-LOCAL_INIT_RC := nativeperms.rc
-include $(BUILD_EXECUTABLE)
diff --git a/services/sensorservice/tests/Android.bp b/services/sensorservice/tests/Android.bp
new file mode 100644
index 0000000..d33c0ca
--- /dev/null
+++ b/services/sensorservice/tests/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+    name: "test-sensorservice",
+    srcs: ["sensorservicetest.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libutils",
+        "libsensor",
+        "libandroid",
+    ],
+}
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
deleted file mode 100644
index 8a9c36b..0000000
--- a/services/sensorservice/tests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	sensorservicetest.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libutils libsensor libandroid
-
-LOCAL_MODULE:= test-sensorservice
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2132f59..83b9585 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -20,6 +20,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "BufferStateLayer.h"
+#include "ColorLayer.h"
 
 #include "TimeStats/TimeStats.h"
 
@@ -302,6 +303,48 @@
     return true;
 }
 
+bool BufferStateLayer::setColor(const half3& color) {
+    // create color layer if one does not yet exist
+    if (!mCurrentState.bgColorLayer) {
+        uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
+        const String8& name = mName + "BackgroundColorLayer";
+        mCurrentState.bgColorLayer =
+                new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags));
+
+        // add to child list
+        addChild(mCurrentState.bgColorLayer);
+        mFlinger->mLayersAdded = true;
+        // set up SF to handle added color layer
+        if (isRemovedFromCurrentState()) {
+            mCurrentState.bgColorLayer->onRemovedFromCurrentState();
+        }
+        mFlinger->setTransactionFlags(eTransactionNeeded);
+    }
+
+    mCurrentState.bgColorLayer->setColor(color);
+    mCurrentState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min());
+
+    return true;
+}
+
+bool BufferStateLayer::setColorAlpha(float alpha) {
+    if (!mCurrentState.bgColorLayer) {
+        ALOGE("Attempting to set color alpha on a buffer state layer with no background color");
+        return false;
+    }
+    mCurrentState.bgColorLayer->setAlpha(alpha);
+    return true;
+}
+
+bool BufferStateLayer::setColorDataspace(ui::Dataspace dataspace) {
+    if (!mCurrentState.bgColorLayer) {
+        ALOGE("Attempting to set color dataspace on a buffer state layer with no background color");
+        return false;
+    }
+    mCurrentState.bgColorLayer->setDataspace(dataspace);
+    return true;
+}
+
 Rect BufferStateLayer::getBufferSize(const State& s) const {
     // for buffer state layers we use the display frame size as the buffer size.
     if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 3f891d3..64d6a85 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -82,6 +82,9 @@
     }
     bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; }
     bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
+    bool setColor(const half3& color) override;
+    bool setColorAlpha(float alpha) override;
+    bool setColorDataspace(ui::Dataspace dataspace) override;
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
     void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f181220..fde90f4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2147,6 +2147,10 @@
 InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
 
+    if (info.displayId == ADISPLAY_ID_NONE) {
+        info.displayId = mDrawingState.layerStack;
+    }
+
     ui::Transform t = getTransform();
     const float xScale = t.sx();
     const float yScale = t.sy();
@@ -2157,7 +2161,10 @@
     }
 
     // Transform layer size to screen space and inset it by surface insets.
-    Rect layerBounds = getBufferSize(getDrawingState());
+    // If this is a portal window, set the touchableRegion to the layerBounds.
+    Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+            ? getBufferSize(getDrawingState())
+            : info.touchableRegion.getBounds();
     if (!layerBounds.isValid()) {
         layerBounds = getCroppedBufferSize(getDrawingState());
     }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d30961a..b3979e2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -201,6 +201,10 @@
         mat4 colorTransform;
         bool hasColorTransform;
 
+        // pointer to background color layer that, if set, appears below the buffer state layer
+        // and the buffer state layer's children.  Z order will be set to
+        // INT_MIN
+        sp<Layer> bgColorLayer;
         ui::Dataspace colorDataspace; // The dataspace of the background color layer
 
         // The deque of callback handles for this frame. The back of the deque contains the most
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 281f6b7..52abe9c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -163,6 +163,7 @@
     if (src == nullptr) {
         mVSyncSource = mVSyncSourceUnique.get();
     }
+    mVSyncSource->setCallback(this);
 
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
         std::unique_lock<std::mutex> lock(mMutex);
@@ -185,9 +186,11 @@
 }
 
 EventThread::~EventThread() {
+    mVSyncSource->setCallback(nullptr);
+
     {
         std::lock_guard<std::mutex> lock(mMutex);
-        mKeepRunning = false;
+        mState = State::Quit;
         mCondition.notify_all();
     }
     mThread.join();
@@ -261,24 +264,29 @@
 
 void EventThread::onScreenReleased() {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mVSyncState.synthetic) {
-        mVSyncState.synthetic = true;
-        mCondition.notify_all();
+    if (!mVSyncState || mVSyncState->synthetic) {
+        return;
     }
+
+    mVSyncState->synthetic = true;
+    mCondition.notify_all();
 }
 
 void EventThread::onScreenAcquired() {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (mVSyncState.synthetic) {
-        mVSyncState.synthetic = false;
-        mCondition.notify_all();
+    if (!mVSyncState || !mVSyncState->synthetic) {
+        return;
     }
+
+    mVSyncState->synthetic = false;
+    mCondition.notify_all();
 }
 
 void EventThread::onVSyncEvent(nsecs_t timestamp) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    mPendingEvents.push_back(makeVSync(mVSyncState.displayId, timestamp, ++mVSyncState.count));
+    LOG_FATAL_IF(!mVSyncState);
+    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
     mCondition.notify_all();
 }
 
@@ -293,20 +301,30 @@
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
-    while (mKeepRunning) {
+    while (mState != State::Quit) {
         std::optional<DisplayEventReceiver::Event> event;
 
         // Determine next event to dispatch.
         if (!mPendingEvents.empty()) {
             event = mPendingEvents.front();
             mPendingEvents.pop_front();
-        }
 
-        const bool vsyncPending =
-                event && event->header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+            switch (event->header.type) {
+                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+                    if (event->hotplug.connected && !mVSyncState) {
+                        mVSyncState.emplace(event->header.id);
+                    } else if (!event->hotplug.connected && mVSyncState &&
+                               mVSyncState->displayId == event->header.id) {
+                        mVSyncState.reset();
+                    }
+                    break;
 
-        if (mInterceptVSyncsCallback && vsyncPending) {
-            mInterceptVSyncsCallback(event->header.timestamp);
+                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                    if (mInterceptVSyncsCallback) {
+                        mInterceptVSyncsCallback(event->header.timestamp);
+                    }
+                    break;
+            }
         }
 
         bool vsyncRequested = false;
@@ -332,19 +350,22 @@
             consumers.clear();
         }
 
-        // Here we figure out if we need to enable or disable vsyncs
-        if (vsyncPending && !vsyncRequested) {
-            // we received a VSYNC but we have no clients
-            // don't report it, and disable VSYNC events
-            disableVSyncLocked();
-        } else if (!vsyncPending && vsyncRequested) {
-            // we have at least one client, so we want vsync enabled
-            // (TODO: this function is called right after we finish
-            // notifying clients of a vsync, so this call will be made
-            // at the vsync rate, e.g. 60fps.  If we can accurately
-            // track the current state we could avoid making this call
-            // so often.)
-            enableVSyncLocked();
+        State nextState;
+        if (mVSyncState && vsyncRequested) {
+            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+        } else {
+            ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
+            nextState = State::Idle;
+        }
+
+        if (mState != nextState) {
+            if (mState == State::VSync) {
+                mVSyncSource->setVSyncEnabled(false);
+            } else if (nextState == State::VSync) {
+                mVSyncSource->setVSyncEnabled(true);
+            }
+
+            mState = nextState;
         }
 
         if (event) {
@@ -352,19 +373,20 @@
         }
 
         // Wait for event or client registration/request.
-        if (vsyncRequested) {
+        if (mState == State::Idle) {
+            mCondition.wait(lock);
+        } else {
             // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
             // display is off, keep feeding clients at 60 Hz.
-            const auto timeout = mVSyncState.synthetic ? 16ms : 1000ms;
+            const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
             if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
-                ALOGW_IF(!mVSyncState.synthetic, "Faking VSYNC due to driver stall");
+                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
 
-                mPendingEvents.push_back(makeVSync(mVSyncState.displayId,
+                LOG_FATAL_IF(!mVSyncState);
+                mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
                                                    systemTime(SYSTEM_TIME_MONOTONIC),
-                                                   ++mVSyncState.count));
+                                                   ++mVSyncState->count));
             }
-        } else {
-            mCondition.wait(lock);
         }
     }
 }
@@ -413,31 +435,16 @@
     }
 }
 
-void EventThread::enableVSyncLocked() {
-    if (!mVSyncState.synthetic) {
-        if (!mVsyncEnabled) {
-            mVsyncEnabled = true;
-            mVSyncSource->setCallback(this);
-            mVSyncSource->setVSyncEnabled(true);
-        }
-    }
-    mDebugVsyncEnabled = true;
-}
-
-void EventThread::disableVSyncLocked() {
-    if (mVsyncEnabled) {
-        mVsyncEnabled = false;
-        mVSyncSource->setVSyncEnabled(false);
-        mDebugVsyncEnabled = false;
-    }
-}
-
 void EventThread::dump(std::string& result) const {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    StringAppendF(&result, "%s: VSYNC %s\n", mThreadName, mDebugVsyncEnabled ? "on" : "off");
-    StringAppendF(&result, "  VSyncState{displayId=%u, count=%u%s}\n", mVSyncState.displayId,
-                  mVSyncState.count, mVSyncState.synthetic ? ", synthetic" : "");
+    StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
+    if (mVSyncState) {
+        StringAppendF(&result, "{displayId=%u, count=%u%s}\n", mVSyncState->displayId,
+                      mVSyncState->count, mVSyncState->synthetic ? ", synthetic" : "");
+    } else {
+        StringAppendF(&result, "none\n");
+    }
 
     StringAppendF(&result, "  pending events (count=%zu):\n", mPendingEvents.size());
     for (const auto& event : mPendingEvents) {
@@ -452,5 +459,18 @@
     }
 }
 
+const char* EventThread::toCString(State state) {
+    switch (state) {
+        case State::Idle:
+            return "Idle";
+        case State::Quit:
+            return "Quit";
+        case State::SyntheticVSync:
+            return "SyntheticVSync";
+        case State::VSync:
+            return "VSync";
+    }
+}
+
 } // namespace impl
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 1a8ebb7..89b799e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -22,6 +22,7 @@
 #include <cstdint>
 #include <deque>
 #include <mutex>
+#include <optional>
 #include <thread>
 #include <vector>
 
@@ -176,9 +177,6 @@
     void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
             REQUIRES(mMutex);
 
-    void enableVSyncLocked() REQUIRES(mMutex);
-    void disableVSyncLocked() REQUIRES(mMutex);
-
     // Implements VSyncSource::Callback
     void onVSyncEvent(nsecs_t timestamp) override;
 
@@ -201,7 +199,9 @@
 
     // VSYNC state of connected display.
     struct VSyncState {
-        uint32_t displayId = 0;
+        explicit VSyncState(uint32_t displayId) : displayId(displayId) {}
+
+        const uint32_t displayId;
 
         // Number of VSYNC events since display was connected.
         uint32_t count = 0;
@@ -210,13 +210,21 @@
         bool synthetic = false;
     };
 
-    VSyncState mVSyncState GUARDED_BY(mMutex);
+    // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
+    // and support headless mode by injecting a fake display with synthetic VSYNC.
+    std::optional<VSyncState> mVSyncState GUARDED_BY(mMutex);
 
-    bool mVsyncEnabled GUARDED_BY(mMutex) = false;
-    bool mKeepRunning GUARDED_BY(mMutex) = true;
+    // State machine for event loop.
+    enum class State {
+        Idle,
+        Quit,
+        SyntheticVSync,
+        VSync,
+    };
 
-    // for debugging
-    bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
+    State mState GUARDED_BY(mMutex) = State::Idle;
+
+    static const char* toCString(State);
 
     // Callback that resets the idle timer when the next vsync is received.
     ResetIdleTimerCallback mResetIdleTimer;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2ed2866..b003451 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -84,14 +84,14 @@
 Scheduler::~Scheduler() = default;
 
 sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
-        const std::string& connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+        const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     const int64_t id = sNextId++;
     ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
 
     std::unique_ptr<EventThread> eventThread =
             makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
-                            interceptCallback);
+                            std::move(interceptCallback));
     auto connection = std::make_unique<Connection>(new ConnectionHandle(id),
                                                    eventThread->createEventConnection(
                                                            std::move(resyncCallback)),
@@ -102,14 +102,13 @@
 }
 
 std::unique_ptr<EventThread> Scheduler::makeEventThread(
-        const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+        const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    const std::string sourceName = connectionName + "Source";
     std::unique_ptr<VSyncSource> eventThreadSource =
-            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, sourceName.c_str());
-    const std::string threadName = connectionName + "Thread";
-    return std::make_unique<impl::EventThread>(std::move(eventThreadSource), interceptCallback,
-                                               [this] { resetIdleTimer(); }, threadName.c_str());
+            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+    return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
+                                               std::move(interceptCallback),
+                                               [this] { resetIdleTimer(); }, connectionName);
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index e992398..b717605 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -73,7 +73,7 @@
 
     /** Creates an EventThread connection. */
     sp<ConnectionHandle> createConnection(
-            const std::string& connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+            const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
             impl::EventThread::InterceptVSyncsCallback interceptCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
@@ -123,7 +123,7 @@
 
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
-            const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+            const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
             impl::EventThread::InterceptVSyncsCallback interceptCallback);
 
 private:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 14feb43..acf0013 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2564,6 +2564,17 @@
     mPendingHotplugEvents.clear();
 }
 
+void SurfaceFlinger::dispatchDisplayHotplugEvent(EventThread::DisplayType displayType,
+                                                 bool connected) {
+    if (mUseScheduler) {
+        mScheduler->hotplugReceived(mAppConnectionHandle, displayType, connected);
+        mScheduler->hotplugReceived(mSfConnectionHandle, displayType, connected);
+    } else {
+        mEventThread->onHotplugReceived(displayType, connected);
+        mSFEventThread->onHotplugReceived(displayType, connected);
+    }
+}
+
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
         const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
         const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
@@ -2668,19 +2679,9 @@
                     display->disconnect();
                 }
                 if (internalDisplayId && internalDisplayId == draw[i].displayId) {
-                    if (mUseScheduler) {
-                        mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                    EventThread::DisplayType::Primary, false);
-                    } else {
-                        mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
-                    }
+                    dispatchDisplayHotplugEvent(EventThread::DisplayType::Primary, false);
                 } else if (externalDisplayId && externalDisplayId == draw[i].displayId) {
-                    if (mUseScheduler) {
-                        mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                    EventThread::DisplayType::External, false);
-                    } else {
-                        mEventThread->onHotplugReceived(EventThread::DisplayType::External, false);
-                    }
+                    dispatchDisplayHotplugEvent(EventThread::DisplayType::External, false);
                 }
                 mDisplays.erase(draw.keyAt(i));
             } else {
@@ -2786,23 +2787,9 @@
                         LOG_ALWAYS_FATAL_IF(!displayId);
 
                         if (displayId == getInternalDisplayId()) {
-                            if (mUseScheduler) {
-                                mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                            EventThread::DisplayType::Primary,
-                                                            true);
-                            } else {
-                                mEventThread->onHotplugReceived(EventThread::DisplayType::Primary,
-                                                                true);
-                            }
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::Primary, true);
                         } else if (displayId == getExternalDisplayId()) {
-                            if (mUseScheduler) {
-                                mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                            EventThread::DisplayType::External,
-                                                            true);
-                            } else {
-                                mEventThread->onHotplugReceived(EventThread::DisplayType::External,
-                                                                true);
-                            }
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::External, true);
                         }
                     }
                 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c62f852..4d84144 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -294,10 +294,6 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    enum {
-        EVENT_VSYNC = HWC_EVENT_VSYNC
-    };
-
     // post an asynchronous message to the main thread
     status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
 
@@ -754,6 +750,8 @@
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
+    void dispatchDisplayHotplugEvent(EventThread::DisplayType displayType, bool connected);
+
     /* ------------------------------------------------------------------------
      * VSync
      */
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 396fc55..56d3bd4 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -26,6 +26,8 @@
 
 #include <android/native_window.h>
 
+#include <binder/ProcessState.h>
+#include <gui/BufferItemConsumer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
@@ -495,10 +497,6 @@
     // leave room for ~256 layers
     const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
 
-    void setRelativeZBasicHelper(uint32_t layerType);
-    void setRelativeZGroupHelper(uint32_t layerType);
-    void setAlphaBasicHelper(uint32_t layerType);
-
     sp<SurfaceControl> mBlackBgSurface;
     bool mColorManagementUsed;
 
@@ -544,15 +542,72 @@
     }
 
     int32_t mBufferPostDelay;
+
+    friend class LayerRenderPathTestHarness;
+};
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+class LayerRenderPathTestHarness {
+public:
+    LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+          : mDelegate(delegate), mRenderPath(renderPath) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        switch (mRenderPath) {
+            case RenderPath::SCREENSHOT:
+                return mDelegate->screenshot();
+            case RenderPath::VIRTUAL_DISPLAY:
+
+                sp<IBinder> mainDisplay =
+                        SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+                DisplayInfo mainDisplayInfo;
+                SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+
+                sp<IBinder> vDisplay;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferConsumer> consumer;
+                sp<BufferItemConsumer> itemConsumer;
+                BufferQueue::createBufferQueue(&producer, &consumer);
+
+                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+
+                itemConsumer = new BufferItemConsumer(consumer,
+                                                      // Sample usage bits from screenrecord
+                                                      GRALLOC_USAGE_HW_VIDEO_ENCODER |
+                                                              GRALLOC_USAGE_SW_READ_OFTEN);
+
+                vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+                                                                false /*secure*/);
+
+                SurfaceComposerClient::Transaction t;
+                t.setDisplaySurface(vDisplay, producer);
+                t.setDisplayLayerStack(vDisplay, 0);
+                t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
+                                       Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
+                                       Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+                t.apply();
+                SurfaceComposerClient::Transaction().apply(true);
+                BufferItem item;
+                itemConsumer->acquireBuffer(&item, 0, true);
+                auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+                itemConsumer->releaseBuffer(item);
+                SurfaceComposerClient::destroyDisplay(vDisplay);
+                return sc;
+        }
+    }
+
+protected:
+    LayerTransactionTest* mDelegate;
+    RenderPath mRenderPath;
 };
 
-class LayerTypeTransactionTest : public LayerTransactionTest,
-                                 public ::testing::WithParamInterface<uint32_t> {
+class LayerTypeTransactionHarness : public LayerTransactionTest {
 public:
-    LayerTypeTransactionTest() { mLayerType = GetParam(); }
+    LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
 
     sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                   uint32_t flags = 0, SurfaceControl* parent = nullptr) override {
+                                   uint32_t flags = 0, SurfaceControl* parent = nullptr) {
         // if the flags already have a layer type specified, return an error
         if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
             return nullptr;
@@ -579,12 +634,69 @@
     uint32_t mLayerType;
 };
 
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+                                 public ::testing::WithParamInterface<uint32_t> {
+public:
+    LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+class LayerTypeAndRenderTypeTransactionTest
+      : public LayerTypeTransactionHarness,
+        public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+    LayerTypeAndRenderTypeTransactionTest()
+          : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+            mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        return mRenderPathHarness.getScreenCapture();
+    }
+
+protected:
+    LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+    void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+                                       public ::testing::WithParamInterface<RenderPath> {
+public:
+    LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+    void setRelativeZBasicHelper(uint32_t layerType);
+    void setRelativeZGroupHelper(uint32_t layerType);
+    void setAlphaBasicHelper(uint32_t layerType);
+
+protected:
+    LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+        ::testing::Combine(
+                ::testing::Values(
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+                ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+                        ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
 INSTANTIATE_TEST_CASE_P(
         LayerTypeTransactionTests, LayerTypeTransactionTest,
         ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
                           static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
 
-TEST_F(LayerTransactionTest, SetPositionBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -592,7 +704,7 @@
     {
         SCOPED_TRACE("default position");
         const Rect rect(0, 0, 32, 32);
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
     }
@@ -601,13 +713,13 @@
     {
         SCOPED_TRACE("new position");
         const Rect rect(5, 10, 37, 42);
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionRounding_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -619,17 +731,17 @@
     Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
     {
         SCOPED_TRACE("rounding down");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
     {
         SCOPED_TRACE("rounding up");
-        screenshot()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -637,17 +749,17 @@
     Transaction().setPosition(layer, -32, -32).apply();
     {
         SCOPED_TRACE("negative coordinates");
-        screenshot()->expectColor(mDisplayRect, Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
     {
         SCOPED_TRACE("positive coordinates");
-        screenshot()->expectColor(mDisplayRect, Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -656,19 +768,19 @@
     Transaction().setPosition(layer, -30, -30).apply();
     {
         SCOPED_TRACE("negative coordinates");
-        screenshot()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
     }
 
     Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
     {
         SCOPED_TRACE("positive coordinates");
-        screenshot()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
-                                       mDisplayHeight),
-                                  Color::RED);
+        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+                                             mDisplayHeight),
+                                        Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -678,7 +790,7 @@
     Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         const Rect rect(5, 10, 37, 42);
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
@@ -687,11 +799,11 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("resize applied");
-        screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithNextResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -700,30 +812,30 @@
     Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply();
     {
         SCOPED_TRACE("new position pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setPosition(layer, 15, 20).apply();
     {
         SCOPED_TRACE("pending new position modified");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     // finally resize and latch the buffer
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("new position applied");
-        screenshot()->expectColor(Rect(15, 20, 79, 84), Color::RED);
+        getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -737,17 +849,17 @@
             .apply();
     {
         SCOPED_TRACE("new position pending");
-        screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("new position applied");
-        screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetSizeBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -755,7 +867,7 @@
     Transaction().setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         const Rect rect(0, 0, 32, 32);
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
@@ -764,18 +876,18 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("resize applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         const Rect rect(0, 0, 64, 64);
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
     }
 }
 
-TEST_P(LayerTypeTransactionTest, SetSizeInvalid) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
     // cannot test robustness against invalid sizes (zero or really huge)
 }
 
-TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -785,10 +897,10 @@
             .setSize(layer, 64, 64)
             .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
 }
 
-TEST_P(LayerTypeTransactionTest, SetZBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -799,17 +911,17 @@
     Transaction().setLayer(layerR, mLayerZBase + 1).apply();
     {
         SCOPED_TRACE("layerR");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setLayer(layerG, mLayerZBase + 2).apply();
     {
         SCOPED_TRACE("layerG");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
     }
 }
 
-TEST_P(LayerTypeTransactionTest, SetZNegative) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
     sp<SurfaceControl> parent =
             LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
@@ -828,19 +940,19 @@
     Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
     {
         SCOPED_TRACE("layerR");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setLayer(layerR, -3).apply();
     {
         SCOPED_TRACE("layerG");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
     }
 }
 
-void LayerTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
@@ -867,7 +979,7 @@
     }
     {
         SCOPED_TRACE("layerG above");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
     }
@@ -875,17 +987,17 @@
     Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
     {
         SCOPED_TRACE("layerG below");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
     }
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
     ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
     ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
 }
 
@@ -918,7 +1030,7 @@
     screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
 }
 
-void LayerTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
@@ -954,7 +1066,7 @@
 
     {
         SCOPED_TRACE("(layerR < layerG) < layerB");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
         shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
         shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
@@ -964,7 +1076,7 @@
     Transaction().setLayer(layerR, mLayerZBase + 4).apply();
     {
         SCOPED_TRACE("layerB < (layerR < layerG)");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
         shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
         shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -974,7 +1086,7 @@
     Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
     {
         SCOPED_TRACE("layerB < (layerG < layerR)");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
         shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -985,7 +1097,7 @@
     Transaction().setLayer(layerG, mLayerZBase).apply();
     {
         SCOPED_TRACE("layerG < layerB < layerR");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
     }
@@ -995,21 +1107,21 @@
     Transaction().setLayer(layerR, mLayerZBase + 1).apply();
     {
         SCOPED_TRACE("layerG < layerR < layerB");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
     }
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
     ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
     ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
 }
 
-TEST_P(LayerTypeTransactionTest, SetRelativeZBug64572777) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
 
@@ -1025,10 +1137,10 @@
 
     layerG->clear();
     // layerG should have been removed
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_P(LayerTypeTransactionTest, SetFlagsHidden) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
@@ -1036,17 +1148,17 @@
     Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
     {
         SCOPED_TRACE("layer hidden");
-        screenshot()->expectColor(mDisplayRect, Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
     {
         SCOPED_TRACE("layer shown");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_P(LayerTypeTransactionTest, SetFlagsOpaque) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
     const Color translucentRed = {100, 0, 0, 100};
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
@@ -1061,14 +1173,14 @@
             .apply();
     {
         SCOPED_TRACE("layerR opaque");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
     }
 
     Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
     {
         SCOPED_TRACE("layerR translucent");
         const uint8_t g = uint8_t(255 - translucentRed.a);
-        screenshot()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
     }
 }
 
@@ -1090,7 +1202,7 @@
               composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
     const Rect top(0, 0, 32, 16);
     const Rect bottom(0, 16, 32, 32);
     sp<SurfaceControl> layer;
@@ -1105,7 +1217,7 @@
     ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
     {
         SCOPED_TRACE("top transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::RED);
     }
@@ -1113,7 +1225,7 @@
     Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
     {
         SCOPED_TRACE("transparent region hint pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::RED);
     }
@@ -1124,13 +1236,13 @@
     ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
     {
         SCOPED_TRACE("bottom transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::RED);
         shot->expectColor(bottom, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
     const Rect top(0, 0, 32, 16);
     const Rect bottom(0, 16, 32, 32);
     sp<SurfaceControl> layer;
@@ -1152,7 +1264,7 @@
             .apply();
     {
         SCOPED_TRACE("top transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::RED);
     }
@@ -1160,7 +1272,7 @@
     Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
     {
         SCOPED_TRACE("transparent region hint intermediate");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::BLACK);
     }
@@ -1175,13 +1287,13 @@
     Transaction().setBuffer(layer, buffer).apply();
     {
         SCOPED_TRACE("bottom transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::RED);
         shot->expectColor(bottom, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layerTransparent;
     sp<SurfaceControl> layerR;
     ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -1196,10 +1308,10 @@
     ASSERT_NO_FATAL_FAILURE(
             fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
-    screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
     sp<SurfaceControl> layerTransparent;
     sp<SurfaceControl> layerR;
     ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -1215,10 +1327,96 @@
     ASSERT_NO_FATAL_FAILURE(
             fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
-    screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
-void LayerTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_Color_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 0, 0, ISurfaceComposerClient::eFXSurfaceColor));
+
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setCrop_legacy(layer, Rect(0, 0, 32, 32))
+            .setAlpha(layer, 1.0f)
+            .setColor(layer, color)
+            .setColorAlpha(layer, 0.5f)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferQueue_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setAlpha(layer, 1.0f).setColorAlpha(layer, 0.5f).apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferState_ColorLayer) {
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(bgLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bgLayer, Color::RED, 32, 32));
+
+    // create color layer
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).setColor(layer, color).apply();
+
+    {
+        SCOPED_TRACE("before alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    // apply alpha
+    Transaction().setAlpha(layer, 0.0f).apply();
+    {
+        SCOPED_TRACE("set alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferState_NoColorLayer) {
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(bgLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bgLayer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("before alpha");
+        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    // setting alpha without creating color layer should have no effect
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).setAlpha(layer, 0.5f).apply();
+    {
+        SCOPED_TRACE("alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
     sp<SurfaceControl> layer1;
     sp<SurfaceControl> layer2;
     ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
@@ -1248,7 +1446,7 @@
             ASSERT_FALSE(true) << "Unsupported layer type";
     }
     {
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         uint8_t r = 16; // 64 * 0.25f
         uint8_t g = 48; // 64 * 0.75f
         shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
@@ -1259,15 +1457,15 @@
     }
 }
 
-TEST_F(LayerTransactionTest, SetAlphaBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
     ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
 }
 
-TEST_F(LayerTransactionTest, SetAlphaBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
     ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
 }
 
-TEST_P(LayerTypeTransactionTest, SetAlphaClamped) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
     const Color color = {64, 0, 0, 255};
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
@@ -1276,17 +1474,17 @@
     Transaction().setAlpha(layer, 2.0f).apply();
     {
         SCOPED_TRACE("clamped to 1.0f");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), color);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
     }
 
     Transaction().setAlpha(layer, -1.0f).apply();
     {
         SCOPED_TRACE("clamped to 0.0f");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
-TEST_P(LayerTypeTransactionTest, SetCornerRadius) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
     sp<SurfaceControl> layer;
     const uint8_t size = 64;
     const uint8_t testArea = 4;
@@ -1300,7 +1498,7 @@
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         // Transparent corners
         shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
         shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
@@ -1309,7 +1507,7 @@
     }
 }
 
-TEST_P(LayerTypeTransactionTest, SetCornerRadiusChildCrop) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
     sp<SurfaceControl> parent;
     sp<SurfaceControl> child;
     const uint8_t size = 64;
@@ -1328,7 +1526,7 @@
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         // Top edge of child should not have rounded corners because it's translated in the parent
         shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
             Color::GREEN);
@@ -1338,7 +1536,7 @@
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
@@ -1354,7 +1552,7 @@
 
     {
         SCOPED_TRACE("default color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
@@ -1365,11 +1563,82 @@
     Transaction().setColor(colorLayer, color).apply();
     {
         SCOPED_TRACE("new color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorClamped) {
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferState_NoPriorColor) {
+    sp<SurfaceControl> bufferQueueLayer;
+    sp<SurfaceControl> bufferStateLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferQueueLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            bufferStateLayer =
+                    createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferQueueLayer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("default color");
+        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(bufferStateLayer, Rect(0, 0, 32, 32))
+            .setLayer(bufferStateLayer, mLayerZBase + 1)
+            .setColor(bufferStateLayer, color)
+            .apply();
+
+    {
+        SCOPED_TRACE("set color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferState_PriorColor) {
+    sp<SurfaceControl> bufferQueueLayer;
+    sp<SurfaceControl> bufferStateLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferQueueLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            bufferStateLayer =
+                    createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferQueueLayer, Color::RED, 32, 32));
+
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(bufferStateLayer, Rect(0, 0, 32, 32))
+            .setLayer(bufferStateLayer, mLayerZBase + 1)
+            .setColor(bufferStateLayer, color)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    color.r = 0.0f;
+    color.g = 0.0f;
+    color.b = 1.0f;
+    Transaction().setColor(bufferStateLayer, color).apply();
+    {
+        SCOPED_TRACE("new color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(colorLayer =
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
@@ -1379,10 +1648,10 @@
             .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
             .apply();
 
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetColorWithAlpha) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
@@ -1403,11 +1672,11 @@
             .setAlpha(colorLayer, alpha)
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                              tolerance);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
 }
 
-TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> parentLayer;
     sp<SurfaceControl> colorLayer;
@@ -1430,21 +1699,21 @@
             .setAlpha(parentLayer, alpha)
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                              tolerance);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
 }
 
-TEST_P(LayerTypeTransactionTest, SetColorWithBuffer) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
     sp<SurfaceControl> bufferLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
 
     // color is ignored
     Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_P(LayerTypeTransactionTest, SetLayerStackBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
@@ -1452,17 +1721,17 @@
     Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
     {
         SCOPED_TRACE("non-existing layer stack");
-        screenshot()->expectColor(mDisplayRect, Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
     {
         SCOPED_TRACE("original layer stack");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1471,40 +1740,40 @@
     Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("IDENTITY");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
     {
         SCOPED_TRACE("FLIP_H");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
-                                     Color::BLUE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+                                           Color::WHITE, Color::BLUE);
     }
 
     Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
     {
         SCOPED_TRACE("FLIP_V");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
-                                     Color::GREEN);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+                                           Color::RED, Color::GREEN);
     }
 
     Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
     {
         SCOPED_TRACE("ROT_90");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
-                                     Color::GREEN);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+                                           Color::WHITE, Color::GREEN);
     }
 
     Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("SCALE");
-        screenshot()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE, true /* filtered */);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1517,40 +1786,40 @@
             .apply();
     {
         SCOPED_TRACE("IDENTITY");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
     {
         SCOPED_TRACE("FLIP_H");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
     {
         SCOPED_TRACE("FLIP_V");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
     {
         SCOPED_TRACE("ROT_90");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
     {
         SCOPED_TRACE("SCALE");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixRot45_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1560,7 +1829,7 @@
     const float trans = M_SQRT2 * 16.0f;
     Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     // check a 8x8 region inside each color
     auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
         const int32_t halfL = 4;
@@ -1573,7 +1842,7 @@
     shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
 }
 
-TEST_F(LayerTransactionTest, SetMatrixWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1582,7 +1851,7 @@
     Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         const Rect rect(0, 0, 32, 32);
         shot->expectColor(rect, Color::RED);
         shot->expectBorder(rect, Color::BLACK);
@@ -1592,11 +1861,11 @@
     {
         SCOPED_TRACE("resize applied");
         const Rect rect(0, 0, 128, 128);
-        screenshot()->expectColor(rect, Color::RED);
+        getScreenCapture()->expectColor(rect, Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1607,10 +1876,10 @@
             .setSize(layer, 64, 64)
             .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1625,8 +1894,8 @@
             .apply();
     {
         SCOPED_TRACE("SCALE_TO_WINDOW");
-        screenshot()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE, true /* filtered */);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
     }
 }
 
@@ -1643,19 +1912,19 @@
     ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
 }
 
-TEST_F(LayerTransactionTest, SetCropBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
     Transaction().setCrop_legacy(layer, crop).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(crop, Color::RED);
     shot->expectBorder(crop, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1663,12 +1932,12 @@
     const Rect crop(8, 8, 24, 24);
 
     Transaction().setCrop(layer, crop).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropEmpty_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1676,17 +1945,17 @@
     {
         SCOPED_TRACE("empty rect");
         Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
         Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropEmpty_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1695,28 +1964,28 @@
     {
         SCOPED_TRACE("empty rect");
         Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
         Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1735,7 +2004,7 @@
     Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply();
     {
         SCOPED_TRACE("out of bounds, negative (upper left) direction");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE);
         shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
     }
@@ -1746,7 +2015,7 @@
             .apply();
     {
         SCOPED_TRACE("out of bounds, positive (lower right) direction");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED);
         shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
     }
@@ -1755,14 +2024,14 @@
     Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
     {
         SCOPED_TRACE("Fully out of bounds");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE);
         shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2),
                           Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1770,12 +2039,12 @@
     const Point position(32, 32);
     const Rect crop(8, 8, 24, 24);
     Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(crop + position, Color::RED);
     shot->expectBorder(crop + position, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1784,12 +2053,12 @@
     const Rect frame(32, 32, 64, 64);
     const Rect crop(8, 8, 24, 24);
     Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(frame, Color::RED);
     shot->expectBorder(frame, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithScale_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1799,12 +2068,12 @@
             .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
             .setCrop_legacy(layer, Rect(8, 8, 24, 24))
             .apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
     shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1813,7 +2082,7 @@
     Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
         shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
     }
@@ -1821,13 +2090,13 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
     {
         SCOPED_TRACE("resize applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
         shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropWithNextResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1839,32 +2108,32 @@
             .apply();
     {
         SCOPED_TRACE("waiting for next resize");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
     {
         SCOPED_TRACE("pending crop modified");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     // finally resize
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
     {
         SCOPED_TRACE("new crop applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
         shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1878,7 +2147,7 @@
             .apply();
     {
         SCOPED_TRACE("new crop pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
     }
@@ -1889,13 +2158,13 @@
     Transaction().setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("new crop applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
         shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFrameBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1903,12 +2172,12 @@
     const Rect frame(8, 8, 24, 24);
 
     Transaction().setFrame(layer, frame).apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(frame, Color::RED);
     shot->expectBorder(frame, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFrameEmpty_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1917,29 +2186,29 @@
     {
         SCOPED_TRACE("empty rect");
         Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     {
         SCOPED_TRACE("negative rect");
         Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFrameDefaultParentless_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
 
     // A parentless layer will default to a frame with the same size as the buffer
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
     shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFrameDefaultBSParent_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1953,12 +2222,12 @@
     Transaction().reparent(child, parent->getHandle()).apply();
 
     // A layer will default to the frame of its parent
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFrameDefaultBQParent_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
@@ -1970,12 +2239,12 @@
     Transaction().reparent(child, parent->getHandle()).apply();
 
     // A layer will default to the frame of its parent
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFrameUpdate_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1986,12 +2255,12 @@
 
     Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
     shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFrameOutsideBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2005,25 +2274,25 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
     Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
     shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetBufferBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetBufferMultipleBuffers_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2032,7 +2301,7 @@
 
     {
         SCOPED_TRACE("set buffer 1");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
@@ -2041,7 +2310,7 @@
 
     {
         SCOPED_TRACE("set buffer 2");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
@@ -2050,13 +2319,13 @@
 
     {
         SCOPED_TRACE("set buffer 3");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetBufferMultipleLayers_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
     sp<SurfaceControl> layer1;
     ASSERT_NO_FATAL_FAILURE(
             layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2070,7 +2339,7 @@
     Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
     {
         SCOPED_TRACE("set layer 1 buffer red");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
@@ -2079,7 +2348,7 @@
     Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
     {
         SCOPED_TRACE("set layer 2 buffer blue");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
         shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
         shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
@@ -2088,7 +2357,7 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
     {
         SCOPED_TRACE("set layer 1 buffer green");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
         shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
         shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
@@ -2098,14 +2367,14 @@
 
     {
         SCOPED_TRACE("set layer 2 buffer white");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
         shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
         shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
     }
 }
 
-TEST_F(LayerTransactionTest, SetTransformRotate90_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2118,11 +2387,11 @@
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
             .apply();
 
-    screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
-                                 Color::GREEN, true /* filtered */);
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+                                       Color::GREEN, true /* filtered */);
 }
 
-TEST_F(LayerTransactionTest, SetTransformFlipH_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2135,11 +2404,11 @@
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
             .apply();
 
-    screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
-                                 Color::BLUE, true /* filtered */);
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+                                       Color::BLUE, true /* filtered */);
 }
 
-TEST_F(LayerTransactionTest, SetTransformFlipV_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2152,8 +2421,8 @@
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
             .apply();
 
-    screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
-                                 Color::GREEN, true /* filtered */);
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+                                       Color::GREEN, true /* filtered */);
 }
 
 TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
@@ -2168,7 +2437,7 @@
     Transaction().setTransformToDisplayInverse(layer, true).apply();
 }
 
-TEST_F(LayerTransactionTest, SetFenceBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
     sp<SurfaceControl> layer;
     Transaction transaction;
     ASSERT_NO_FATAL_FAILURE(
@@ -2193,12 +2462,12 @@
     ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
     std::this_thread::sleep_for(200ms);
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFenceNull_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2217,12 +2486,77 @@
             .setAcquireFence(layer, fence)
             .apply();
 
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_ColorLayer_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 0, 0, ISurfaceComposerClient::eFXSurfaceColor));
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setCrop_legacy(layer, Rect(0, 0, 32, 32))
+            .setColor(layer, color)
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferQueue_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction()
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferState_ColorLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setColor(layer, color)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
     auto shot = screenshot();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetDataspaceBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferState_NoColorLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::DCI_P3)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2239,12 +2573,12 @@
             .setDataspace(layer, ui::Dataspace::UNKNOWN)
             .apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetHdrMetadataBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2263,12 +2597,12 @@
             .setHdrMetadata(layer, hdrMetadata)
             .apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2287,12 +2621,12 @@
             .setSurfaceDamageRegion(layer, region)
             .apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetApiBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2309,7 +2643,7 @@
             .setApi(layer, NATIVE_WINDOW_API_CPU)
             .apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
@@ -2518,7 +2852,7 @@
     }
 };
 
-TEST_F(LayerTransactionTest, SetColorTransformBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(colorLayer =
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
@@ -2529,7 +2863,7 @@
             .apply();
     {
         SCOPED_TRACE("default color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2561,11 +2895,11 @@
         .setColorTransform(colorLayer, matrix, vec3()).apply();
     {
         SCOPED_TRACE("new color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorTransformOnParent) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
     sp<SurfaceControl> parentLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
@@ -2582,7 +2916,7 @@
             .apply();
     {
         SCOPED_TRACE("default color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2616,11 +2950,11 @@
             .apply();
     {
         SCOPED_TRACE("new color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorTransformOnChildAndParent) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
     sp<SurfaceControl> parentLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
@@ -2637,7 +2971,7 @@
             .apply();
     {
         SCOPED_TRACE("default color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2677,7 +3011,7 @@
             .apply();
     {
         SCOPED_TRACE("new color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 8201704..2c1833b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -118,6 +118,7 @@
 
     TestableSurfaceFlinger mFlinger;
     mock::EventThread* mEventThread = new mock::EventThread();
+    mock::EventThread* mSFEventThread = new mock::EventThread();
     mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
     sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -161,6 +162,7 @@
 
     mFlinger.mutableEventControlThread().reset(mEventControlThread);
     mFlinger.mutableEventThread().reset(mEventThread);
+    mFlinger.mutableSFEventThread().reset(mSFEventThread);
     mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
@@ -319,6 +321,11 @@
     // Whether the display is primary
     static constexpr Primary PRIMARY = primary;
 
+    static constexpr auto displayType() {
+        return static_cast<bool>(PRIMARY) ? EventThread::DisplayType::Primary
+                                          : EventThread::DisplayType::External;
+    }
+
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto injector =
                 FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
@@ -1404,23 +1411,17 @@
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  true))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
 }
 
 template <typename Case>
 void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  false))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
 }
 
 template <typename Case>
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index c18068f..dd90063 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -83,6 +83,7 @@
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
     MockVSyncSource mVSyncSource;
+    VSyncSource::Callback* mCallback = nullptr;
     std::unique_ptr<android::impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
@@ -103,12 +104,19 @@
 
     createThread();
     mConnection = createConnection(mConnectionEventCallRecorder);
+
+    // A display must be connected for VSYNC events to be delivered.
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, true);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true);
 }
 
 EventThreadTest::~EventThreadTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    // EventThread should unregister itself as VSyncSource callback.
+    EXPECT_FALSE(expectVSyncSetCallbackCallReceived());
 }
 
 void EventThreadTest::createThread() {
@@ -116,6 +124,10 @@
             std::make_unique<android::impl::EventThread>(&mVSyncSource,
                                                          mInterceptVSyncCallRecorder.getInvocable(),
                                                          "unit-test-event-thread");
+
+    // EventThread should register itself as VSyncSource callback.
+    mCallback = expectVSyncSetCallbackCallReceived();
+    ASSERT_TRUE(mCallback);
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
@@ -199,6 +211,17 @@
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
+TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false);
+
+    // Signal that we want the next vsync event to be posted to the connection.
+    mThread->requestNextVsync(mConnection, false);
+
+    // EventThread should not enable vsync callbacks.
+    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection, false);
@@ -206,22 +229,19 @@
     // EventThread should immediately request a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Use the received callback to signal a first vsync event.
     // The interceptor should receive the event, as well as the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
     // The interceptor should receive the event, but the the connection should
     // not as it was only interested in the first.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -246,16 +266,13 @@
             createConnection(secondConnectionEventRecorder);
     mThread->setVsyncRate(1, secondConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the second connection. The first connection should not
     // get the event.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -265,25 +282,22 @@
 TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
     mThread->setVsyncRate(1, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    callback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789);
     expectInterceptCallReceived(789);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -291,29 +305,26 @@
 TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
     mThread->setVsyncRate(2, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The second event will be seen by the interceptor and the connection.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // The third event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789);
     expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The fourth event will be seen by the interceptor and the connection.
-    callback->onVSyncEvent(101112);
+    mCallback->onVSyncEvent(101112);
     expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
@@ -321,17 +332,14 @@
 TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
     mThread->setVsyncRate(1, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
     // The first event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -344,21 +352,18 @@
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an error.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor and not by the
     // connection.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
@@ -371,21 +376,18 @@
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an non-fatal error.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor, and by the connection,
     // which still then returns an non-fatal error.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 3dd5143..4d9aec6 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -41,7 +41,7 @@
               : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
 
         std::unique_ptr<EventThread> makeEventThread(
-                const std::string& /* connectionName */, DispSync* /* dispSync */,
+                const char* /* connectionName */, DispSync* /* dispSync */,
                 nsecs_t /* phaseOffsetNs */,
                 impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
             return std::move(mEventThread);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2a8dda6..9c5c967 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -290,6 +290,7 @@
     auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
     auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableSFEventThread() { return mFlinger->mSFEventThread; }
     auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
     auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
@@ -317,6 +318,7 @@
         mutableEventControlThread().reset();
         mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableSFEventThread().reset();
         mutableInterceptor().reset();
         mutablePrimaryDispSync().reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 96ef1a2..45593ad 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -69,7 +69,7 @@
 
   bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
                        uint32_t format, uint64_t usage,
-                       size_t user_metadata_size);
+                       size_t user_metadata_size) const;
 
  private:
   std::vector<ConsumerChannel*> consumer_channels_;
@@ -112,6 +112,10 @@
   // This function is used for clean up for failures in CreateConsumer method.
   void RemoveConsumerClientMask(uint32_t consumer_state_mask);
 
+  // Checks whether the buffer is released by all active clients, excluding
+  // orphaned consumers.
+  bool IsBufferReleasedByAllActiveClientsExceptForOrphans() const;
+
   ProducerChannel(const ProducerChannel&) = delete;
   void operator=(const ProducerChannel&) = delete;
 };
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 895dee0..164d9e6 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -333,7 +333,10 @@
 
   uint32_t current_buffer_state =
       buffer_state_->load(std::memory_order_acquire);
-  if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+  // Return the consumer channel handle without signal when adding the new
+  // consumer to a buffer that is available to producer (a.k.a a fully-released
+  // buffer) or a gained buffer.
+  if (current_buffer_state == 0U ||
       BufferHubDefs::AnyClientGained(current_buffer_state)) {
     return {status.take()};
   }
@@ -356,7 +359,7 @@
           ". About to try again if the buffer is still not gained nor fully "
           "released.",
           __FUNCTION__, current_buffer_state, updated_buffer_state);
-      if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+      if (current_buffer_state == 0U ||
           BufferHubDefs::AnyClientGained(current_buffer_state)) {
         ALOGI("%s: buffer is gained or fully released, state=%" PRIx32 ".",
               __FUNCTION__, current_buffer_state);
@@ -536,14 +539,7 @@
     }
   }
 
-  uint32_t current_buffer_state =
-      buffer_state_->load(std::memory_order_acquire);
-  uint32_t current_active_clients_bit_mask =
-      active_clients_bit_mask_->load(std::memory_order_acquire);
-  // Signal producer if all current active consumers have released the buffer.
-  if (BufferHubDefs::IsBufferReleased(current_buffer_state &
-                                      ~orphaned_consumer_bit_mask_ &
-                                      current_active_clients_bit_mask)) {
+  if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
     buffer_state_->store(0U);
     SignalAvailable();
     if (orphaned_consumer_bit_mask_) {
@@ -568,14 +564,7 @@
            __FUNCTION__, consumer_state_mask);
   orphaned_consumer_bit_mask_ |= consumer_state_mask;
 
-  uint32_t current_buffer_state =
-      buffer_state_->load(std::memory_order_acquire);
-  uint32_t current_active_clients_bit_mask =
-      active_clients_bit_mask_->load(std::memory_order_acquire);
-  // Signal producer if all current active consumers have released the buffer.
-  if (BufferHubDefs::IsBufferReleased(current_buffer_state &
-                                      ~orphaned_consumer_bit_mask_ &
-                                      current_active_clients_bit_mask)) {
+  if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
     buffer_state_->store(0U);
     SignalAvailable();
   }
@@ -667,12 +656,19 @@
 bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
                                       uint32_t layer_count, uint32_t format,
                                       uint64_t usage,
-                                      size_t user_metadata_size) {
+                                      size_t user_metadata_size) const {
   return user_metadata_size == user_metadata_size_ &&
          buffer_.width() == width && buffer_.height() == height &&
          buffer_.layer_count() == layer_count && buffer_.format() == format &&
          buffer_.usage() == usage;
 }
 
+bool ProducerChannel::IsBufferReleasedByAllActiveClientsExceptForOrphans()
+    const {
+  return (buffer_state_->load(std::memory_order_acquire) &
+          ~orphaned_consumer_bit_mask_ &
+          active_clients_bit_mask_->load(std::memory_order_acquire)) == 0U;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/vulkan/libvulkan/layers_extensions.h b/vulkan/libvulkan/layers_extensions.h
index 1dae456..9e2ff5b 100644
--- a/vulkan/libvulkan/layers_extensions.h
+++ b/vulkan/libvulkan/layers_extensions.h
@@ -33,6 +33,7 @@
     LayerRef& operator=(const LayerRef&) = delete;
 
     // provides bool-like behavior
+    // NOLINTNEXTLINE(google-explicit-constructor)
     operator const Layer*() const { return layer_; }
 
     PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;