Merge "Revert "Revert "Add plugin API to use cas@1.1 new interface"""
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 99f3739..a282424 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -107,6 +107,10 @@
     chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable
     chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable
     chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable
+    chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
+    chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable
 
     # disk
     chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index e938b10..ee32cb4 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -57,7 +57,6 @@
         export_aidl_headers: true,
     },
     srcs: [
-        "binder/android/os/DumpstateOptions.cpp",
         ":dumpstate_aidl",
     ],
     export_include_dirs: ["binder"],
@@ -68,7 +67,6 @@
     srcs: [
         "binder/android/os/IDumpstateListener.aidl",
         "binder/android/os/IDumpstateToken.aidl",
-        //"binder/android/os/DumpstateOptions.aidl",
         "binder/android/os/IDumpstate.aidl",
     ],
     path: "binder",
@@ -159,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/binder/android/os/DumpstateOptions.aidl b/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
deleted file mode 100644
index c1a7f15..0000000
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-/**
-  * Specifies arguments for IDumpstate.
-  * {@hide}
-  */
-parcelable DumpstateOptions cpp_header "android/os/DumpstateOptions.h";
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp b/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp
deleted file mode 100644
index 5654190..0000000
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/os/DumpstateOptions.h>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-
-namespace android {
-namespace os {
-
-status_t DumpstateOptions::readFromParcel(const ::android::Parcel* parcel) {
-    if (status_t err = parcel->readBool(&get_section_details)) {
-        return err;
-    }
-    if (status_t err = parcel->readUtf8FromUtf16(&name)) {
-        return err;
-    }
-    return android::OK;
-}
-
-status_t DumpstateOptions::writeToParcel(::android::Parcel* parcel) const {
-    if (status_t err = parcel->writeBool(get_section_details)) {
-        return err;
-    }
-    if (status_t err = parcel->writeUtf8AsUtf16(name)) {
-        return err;
-    }
-    return android::OK;
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.h b/cmds/dumpstate/binder/android/os/DumpstateOptions.h
deleted file mode 100644
index a748e3c..0000000
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_OS_DUMPSTATE_OPTIONS_H_
-#define ANDROID_OS_DUMPSTATE_OPTIONS_H_
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace os {
-
-struct DumpstateOptions : public android::Parcelable {
-    // If true the caller can get callbacks with per-section progress details.
-    bool get_section_details = false;
-
-    // Name of the caller.
-    std::string name;
-
-    status_t writeToParcel(android::Parcel* parcel) const override;
-    status_t readFromParcel(const android::Parcel* parcel) override;
-};
-
-}  // namespace os
-}  // namespace android
-
-#endif  // ANDROID_OS_DUMPSTATE_OPTIONS_H_
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index f58535e..b1005d3 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -18,7 +18,6 @@
 
 import android.os.IDumpstateListener;
 import android.os.IDumpstateToken;
-import android.os.DumpstateOptions;
 
 /**
   * Binder interface for the currently running dumpstate process.
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/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
index f68b862..f99588f 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -20,8 +20,6 @@
     static_libs: [
         "libserviceutils",
     ],
-
-    clang: true,
 }
 
 //
@@ -36,7 +34,6 @@
     export_include_dirs: ["."],
 }
 
-
 //
 // Executable
 //
@@ -51,4 +48,15 @@
     ],
 }
 
-subdirs = ["tests"]
+cc_binary {
+    name: "dumpsys_vendor",
+    stem: "dumpsys",
+
+    vendor: true,
+
+    defaults: ["dumpsys_defaults"],
+
+    srcs: [
+        "main.cpp",
+    ],
+}
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 cbf0e09..49383e5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,6 +77,9 @@
 namespace android {
 namespace installd {
 
+// An uuid used in unit tests.
+static constexpr const char* kTestUuid = "TEST";
+
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
 
@@ -155,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();
@@ -207,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));        \
@@ -801,23 +820,12 @@
     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.
-//
-// TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we
-// should validate that volumeUuid is either nullptr or TEST, we won't support
-// anything else.
-//
-// TODO(narayan): We need to be clearer about the expected behaviour for the
-// case where a snapshot already exists. We either need to clear the contents
-// of the snapshot directory before we make a copy, or we need to ensure that
-// the caller always clears it before requesting a snapshot.
 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(volumeUuid);
+    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
@@ -825,6 +833,10 @@
     const char* package_name = packageName.c_str();
 
     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;
 
@@ -853,10 +865,34 @@
         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);
 
+        int rd = delete_dir_contents(to, true /* ignore_if_missing */);
+        if (rd != 0) {
+            res = error(rd, "Failed clearing existing snapshot " + to);
+            return res;
+        }
+
         int rc = copy_directory_recursive(from.c_str(), to.c_str());
         if (rc != 0) {
             res = error(rc, "Failed copying " + from + " to " + to);
@@ -868,12 +904,29 @@
     if (storageFlags & FLAG_STORAGE_CE) {
         auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
         auto to = create_data_misc_ce_rollback_path(volume_uuid, user);
+
+        int rd = delete_dir_contents(to, true /* ignore_if_missing */);
+        if (rd != 0) {
+            res = error(rd, "Failed clearing existing snapshot " + to);
+            return res;
+        }
+
         int rc = copy_directory_recursive(from.c_str(), to.c_str());
         if (rc != 0) {
             res = error(rc, "Failed copying " + from + " to " + to);
             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;
@@ -884,7 +937,7 @@
         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(volumeUuid);
+    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
@@ -929,9 +982,13 @@
         auto to_de = create_data_user_de_path(volume_uuid, user);
         int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str());
         if (rc != 0) {
-            // TODO(narayan): Should we clear clear the rolled back CE data if
-            // something goes wrong here ? We're choosing between leaving the
-            // app devoid of all its data or with just its ce data installed.
+            if (needs_ce_rollback) {
+                auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name);
+                LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data;
+                if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) {
+                    LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data;
+                }
+            }
             res = error(rc, "Failed copying " + from_de + " to " + to_de);
             return res;
         }
@@ -941,6 +998,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,
@@ -2056,8 +2146,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) {
@@ -2089,7 +2185,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/binder_test_utils.h b/cmds/installd/tests/binder_test_utils.h
new file mode 100644
index 0000000..efd1391
--- /dev/null
+++ b/cmds/installd/tests/binder_test_utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Status.h>
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+
+#define ASSERT_BINDER_SUCCESS(expr)                                              \
+    ({                                                                           \
+        binder::Status expect_status = (expr);                                   \
+        ASSERT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str();  \
+        expect_status;                                                           \
+    })
+#define ASSERT_BINDER_FAIL(expr)                \
+    ({                                          \
+        binder::Status expect_status = (expr);  \
+        ASSERT_FALSE(expect_status.isOk());     \
+        expect_status;                          \
+    })
+#define EXPECT_BINDER_SUCCESS(expr)                                              \
+    ({                                                                           \
+        binder::Status expect_status = (expr);                                   \
+        EXPECT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str();  \
+        expect_status;                                                           \
+    })
+#define EXPECT_BINDER_FAIL(expr)                \
+    ({                                          \
+        binder::Status expect_status = (expr);  \
+        EXPECT_FALSE(expect_status.isOk());     \
+        expect_status;                          \
+    })
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 79e6859..e2afe1d 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -25,7 +25,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-
+#include <binder/Status.h>
 #include <cutils/properties.h>
 
 #include <gtest/gtest.h>
@@ -33,6 +33,7 @@
 #include <selinux/android.h>
 #include <selinux/avc.h>
 
+#include "binder_test_utils.h"
 #include "dexopt.h"
 #include "InstalldNativeService.h"
 #include "globals.h"
@@ -451,11 +452,9 @@
         std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
 
         bool prof_result;
-        binder::Status prof_binder_result = service_->prepareAppProfile(
+        ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
                 package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
-                /*dex_metadata*/ nullptr, &prof_result);
-
-        ASSERT_TRUE(prof_binder_result.isOk()) << prof_binder_result.toString8().c_str();
+                /*dex_metadata*/ nullptr, &prof_result));
         ASSERT_TRUE(prof_result);
 
         binder::Status result = service_->dexopt(apk_path_,
@@ -760,9 +759,8 @@
     void createProfileSnapshot(int32_t appid, const std::string& package_name,
             bool expected_result) {
         bool result;
-        binder::Status binder_result = service_->createProfileSnapshot(
-                appid, package_name, kPrimaryProfile, apk_path_, &result);
-        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+        ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot(
+                appid, package_name, kPrimaryProfile, apk_path_, &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
@@ -802,9 +800,8 @@
                               const std::string& code_path,
                               bool expected_result) {
         bool result;
-        binder::Status binder_result = service_->mergeProfiles(
-                kTestAppUid, package_name, code_path, &result);
-        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+        ASSERT_BINDER_SUCCESS(service_->mergeProfiles(
+                kTestAppUid, package_name, code_path, &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
@@ -830,10 +827,9 @@
     void preparePackageProfile(const std::string& package_name, const std::string& profile_name,
             bool expected_result) {
         bool result;
-        binder::Status binder_result = service_->prepareAppProfile(
+        ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
                 package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
-                /*dex_metadata*/ nullptr, &result);
-        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+                /*dex_metadata*/ nullptr, &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
@@ -919,8 +915,7 @@
     SetupProfiles(/*setup_ref*/ true);
     createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
 
-    binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, kPrimaryProfile);
-    ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+    ASSERT_BINDER_SUCCESS(service_->destroyProfileSnapshot(package_name_, kPrimaryProfile));
     struct stat st;
     ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st));
     ASSERT_EQ(ENOENT, errno);
@@ -978,7 +973,7 @@
     ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700));
 
     // Run createAppData again which will offer to fix-up the profile directories.
-    ASSERT_TRUE(service_->createAppData(
+    ASSERT_BINDER_SUCCESS(service_->createAppData(
             volume_uuid_,
             package_name_,
             kTestUserId,
@@ -986,7 +981,7 @@
             kTestAppUid,
             se_info_,
             kOSdkVersion,
-            &ce_data_inode_).isOk());
+            &ce_data_inode_));
 
     // Check the file access.
     CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
@@ -1032,9 +1027,8 @@
 
     void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) {
         bool result;
-        binder::Status binder_result = service_->createProfileSnapshot(
-                -1, "android", "android.prof", classpath, &result);
-        ASSERT_TRUE(binder_result.isOk());
+        ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot(
+                -1, "android", "android.prof", classpath, &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index e6017cb..e2c5907 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -15,18 +15,23 @@
  */
 
 #include <sstream>
+#include <string>
+
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/statvfs.h>
 #include <sys/xattr.h>
 
-#include <android-base/logging.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
 
+#include "binder_test_utils.h"
 #include "InstalldNativeService.h"
 #include "dexopt.h"
 #include "globals.h"
@@ -164,8 +169,8 @@
 
     std::vector<uint8_t> result;
     std::string dexPath = get_full_path("com.example/foo/file");
-    EXPECT_TRUE(service->hashSecondaryDexFile(
-        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+    EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
     EXPECT_EQ(result.size(), 32U);
 
@@ -184,8 +189,8 @@
 
     std::vector<uint8_t> result;
     std::string dexPath = get_full_path("com.example/foo/file");
-    EXPECT_TRUE(service->hashSecondaryDexFile(
-        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+    EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
     EXPECT_EQ(result.size(), 0U);
 }
@@ -199,8 +204,8 @@
 
     std::vector<uint8_t> result;
     std::string dexPath = get_full_path("com.example/foo/file");
-    EXPECT_TRUE(service->hashSecondaryDexFile(
-        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+    EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
     EXPECT_EQ(result.size(), 0U);
 }
@@ -214,8 +219,8 @@
 
     std::vector<uint8_t> result;
     std::string dexPath = get_full_path("com.example/foo/file");
-    EXPECT_FALSE(service->hashSecondaryDexFile(
-        dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+    EXPECT_BINDER_FAIL(service->hashSecondaryDexFile(
+        dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result));
 }
 
 TEST_F(ServiceTest, CalculateOat) {
@@ -287,8 +292,13 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // Request a snapshot of the CE content but not the DE content.
-  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_CE).isOk());
+  int64_t ce_snapshot_inode;
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+  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.
@@ -305,8 +315,10 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // 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());
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode));
+  // 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.
@@ -324,8 +336,8 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // 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());
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
 
   ASSERT_TRUE(android::base::ReadFileToString(
       rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
@@ -351,10 +363,13 @@
 
   auto scope_guard = android::base::make_scope_guard(deleter);
 
-  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_CE).isOk());
-  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
-          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+  int64_t ce_snapshot_inode;
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+  // No CE content snapshot was performed.
+  ASSERT_EQ(ce_snapshot_inode, 0);
 
   // The snapshot calls must succeed but there should be no snapshot
   // created.
@@ -363,6 +378,131 @@
   ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
 }
 
+TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) {
+  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);
+
+  // Simulate presence of an existing snapshot
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", rollback_ce_dir + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", rollback_de_dir + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  // Create app data.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_2_CE", fake_package_ce_path + "/file2",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_2_DE", fake_package_de_path + "/file2",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+
+  // Previous snapshot (with data for file1) must be cleared.
+  struct stat sb;
+  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo/file1").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) {
+  // Setup app data to make sure that 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 deleter = [&rollback_ce_dir, &rollback_de_dir]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+          "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+}
+
+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_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
+  // 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) {
   auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
   auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
@@ -404,8 +544,8 @@
           "TEST_CONTENT_DE", fake_package_de_path + "/file1",
           0700, 10000, 20000, false /* follow_symlinks */));
 
-  ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
-          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+  ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
 
   std::string ce_content, de_content;
   ASSERT_TRUE(android::base::ReadFileToString(
@@ -416,6 +556,163 @@
   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
+  // 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);
+
+  EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE));
+}
 
 }  // namespace installd
 }  // namespace android
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/tests/test_utils.h b/cmds/installd/tests/test_utils.h
index a7ef674..70eefe2 100644
--- a/cmds/installd/tests/test_utils.h
+++ b/cmds/installd/tests/test_utils.h
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
 #include <stdlib.h>
 #include <string.h>
 #include <sys/capability.h>
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/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index 0ce50f2..59bd292 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -183,10 +183,10 @@
             kOfflineLicenseStateUnknown,
             // Offline license state is usable, the keys may be used for decryption.
             kOfflineLicenseStateUsable,
-            // Offline license state is inactive, the keys have been marked for
+            // Offline license state is released, the keys have been marked for
             // release using getKeyRequest() with kKeyType_Release but the
             // key response has not been received.
-            kOfflineLicenseStateInactive
+            kOfflineLicenseStateReleased
         };
 
         DrmPlugin() {}
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index 152015b..ce30b41 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -911,6 +911,9 @@
         case OMX_VIDEO_AVCLevel5:  return "Level5";
         case OMX_VIDEO_AVCLevel51: return "Level51";
         case OMX_VIDEO_AVCLevel52: return "Level52";
+        case OMX_VIDEO_AVCLevel6:  return "Level6";
+        case OMX_VIDEO_AVCLevel61: return "Level61";
+        case OMX_VIDEO_AVCLevel62: return "Level62";
         default:                   return def;
     }
 }
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index 9fd2fd2..b6edaa9 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -832,6 +832,9 @@
     OMX_VIDEO_AVCLevel5   = 0x4000,   /**< Level 5 */
     OMX_VIDEO_AVCLevel51  = 0x8000,   /**< Level 5.1 */
     OMX_VIDEO_AVCLevel52  = 0x10000,  /**< Level 5.2 */
+    OMX_VIDEO_AVCLevel6   = 0x20000,  /**< Level 6 */
+    OMX_VIDEO_AVCLevel61  = 0x40000,  /**< Level 6.1 */
+    OMX_VIDEO_AVCLevel62  = 0x80000,  /**< Level 6.2 */
     OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
     OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
     OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF
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/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index d68f274..640bf1f 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -35,6 +35,14 @@
             mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) {
     }
 
+    bool operator==(const TouchVideoFrame& rhs) const {
+        return mWidth == rhs.mWidth
+                && mHeight == rhs.mHeight
+                && mData == rhs.mData
+                && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec
+                && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec;
+    }
+
     /**
      * Width of the frame
      */
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 05655c1..68011bb 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,12 +39,17 @@
     ],
 
     shared_libs: [
-        "libandroid_runtime",
         "libbase",
         "libbinder",
         "libutils",
     ],
 
+    required: [
+        // libbinder_ndk may be used by Java and non-Java things. When lower-level things use it,
+        // they shouldn't have to take on the cost of loading libandroid_runtime.
+        "libandroid_runtime",
+    ],
+
     version_script: "libbinder_ndk.map.txt",
     stubs: {
         symbol_file: "libbinder_ndk.map.txt",
@@ -60,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/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp
index baea2e8..4a31080 100644
--- a/libs/binder/ndk/ibinder_jni.cpp
+++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -17,15 +17,68 @@
 #include <android/binder_ibinder_jni.h>
 #include "ibinder_internal.h"
 
-#include <android_util_Binder.h>
+#include <android-base/logging.h>
+#include <binder/IBinder.h>
+
+#include <mutex>
+
+#include <dlfcn.h>
 
 using ::android::IBinder;
-using ::android::ibinderForJavaObject;
-using ::android::javaObjectForIBinder;
 using ::android::sp;
 
+struct LazyAndroidRuntime {
+    typedef sp<IBinder> (*FromJava)(JNIEnv* env, jobject obj);
+    typedef jobject (*ToJava)(JNIEnv* env, const sp<IBinder>& val);
+
+    static FromJava ibinderForJavaObject;
+    static ToJava javaObjectForIBinder;
+
+    static void load() {
+        std::call_once(mLoadFlag, []() {
+            void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
+            if (handle == nullptr) {
+                LOG(WARNING) << "Could not open libandroid_runtime.";
+                return;
+            }
+
+            ibinderForJavaObject = reinterpret_cast<FromJava>(
+                    dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
+            if (ibinderForJavaObject == nullptr) {
+                LOG(WARNING) << "Could not find ibinderForJavaObject.";
+                // no return
+            }
+
+            javaObjectForIBinder = reinterpret_cast<ToJava>(dlsym(
+                    handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
+            if (javaObjectForIBinder == nullptr) {
+                LOG(WARNING) << "Could not find javaObjectForIBinder.";
+                // no return
+            }
+        });
+    }
+
+   private:
+    static std::once_flag mLoadFlag;
+
+    LazyAndroidRuntime(){};
+};
+
+LazyAndroidRuntime::FromJava LazyAndroidRuntime::ibinderForJavaObject = nullptr;
+LazyAndroidRuntime::ToJava LazyAndroidRuntime::javaObjectForIBinder = nullptr;
+std::once_flag LazyAndroidRuntime::mLoadFlag;
+
 AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
-    sp<IBinder> ibinder = ibinderForJavaObject(env, binder);
+    if (binder == nullptr) {
+        return nullptr;
+    }
+
+    LazyAndroidRuntime::load();
+    if (LazyAndroidRuntime::ibinderForJavaObject == nullptr) {
+        return nullptr;
+    }
+
+    sp<IBinder> ibinder = (LazyAndroidRuntime::ibinderForJavaObject)(env, binder);
 
     sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder);
     AIBinder_incStrong(cbinder.get());
@@ -38,5 +91,10 @@
         return nullptr;
     }
 
-    return javaObjectForIBinder(env, binder->getBinder());
+    LazyAndroidRuntime::load();
+    if (LazyAndroidRuntime::javaObjectForIBinder == nullptr) {
+        return nullptr;
+    }
+
+    return (LazyAndroidRuntime::javaObjectForIBinder)(env, binder->getBinder());
 }
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 80773f3..c6868b0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -109,6 +109,8 @@
     AIBinder* mBinder = nullptr;
 };
 
+namespace impl {
+
 /**
  * This baseclass owns a single object, used to make various classes RAII.
  */
@@ -169,10 +171,12 @@
     T mT;
 };
 
+}  // namespace impl
+
 /**
  * Convenience wrapper. See AParcel.
  */
-class ScopedAParcel : public ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
+class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -185,7 +189,7 @@
 /**
  * Convenience wrapper. See AStatus.
  */
-class ScopedAStatus : public ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
+class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -209,8 +213,8 @@
  * Convenience wrapper. See AIBinder_DeathRecipient.
  */
 class ScopedAIBinder_DeathRecipient
-    : public ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
-                             nullptr> {
+    : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
+                                   nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -225,7 +229,7 @@
  * Convenience wrapper. See AIBinder_Weak.
  */
 class ScopedAIBinder_Weak
-    : public ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
+    : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -243,7 +247,7 @@
 /**
  * Convenience wrapper for a file descriptor.
  */
-class ScopedFileDescriptor : public ScopedAResource<int, int, close, -1> {
+class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> {
    public:
     /**
      * Takes ownership of a.
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index aa2f394..b8c6cfe 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -30,7 +30,6 @@
 #include <sys/prctl.h>
 
 #include <memory>
-#include <mutex>
 #include <string>
 
 #include <dlfcn.h>
@@ -406,19 +405,26 @@
 }
 
 android_namespace_t* GraphicsEnv::getAngleNamespace() {
-    static std::once_flag once;
-    std::call_once(once, [this]() {
-        if (mAnglePath.empty()) return;
+    std::lock_guard<std::mutex> lock(mNamespaceMutex);
 
-        mAngleNamespace = android_create_namespace("ANGLE",
-                                                   nullptr,            // ld_library_path
-                                                   mAnglePath.c_str(), // default_library_path
-                                                   ANDROID_NAMESPACE_TYPE_SHARED |
-                                                           ANDROID_NAMESPACE_TYPE_ISOLATED,
-                                                   nullptr, // permitted_when_isolated_path
-                                                   nullptr);
-        if (!mAngleNamespace) ALOGD("Could not create ANGLE namespace from default");
-    });
+    if (mAngleNamespace) {
+        return mAngleNamespace;
+    }
+
+    if (mAnglePath.empty()) {
+        ALOGV("mAnglePath is empty, not creating ANGLE namespace");
+        return nullptr;
+    }
+
+    mAngleNamespace = android_create_namespace("ANGLE",
+                                               nullptr,            // ld_library_path
+                                               mAnglePath.c_str(), // default_library_path
+                                               ANDROID_NAMESPACE_TYPE_SHARED |
+                                                       ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                               nullptr, // permitted_when_isolated_path
+                                               nullptr);
+
+    ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
 
     return mAngleNamespace;
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 429ce0e..ed08882 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_UI_GRAPHICS_ENV_H
 #define ANDROID_UI_GRAPHICS_ENV_H 1
 
+#include <mutex>
 #include <string>
 #include <vector>
 
@@ -77,6 +78,7 @@
     std::string mDebugLayers;
     std::string mDebugLayersGLES;
     std::string mLayerPaths;
+    std::mutex mNamespaceMutex;
     android_namespace_t* mDriverNamespace = nullptr;
     android_namespace_t* mAngleNamespace = nullptr;
     NativeLoaderNamespace* mAppNamespace = nullptr;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3521e89..f40eb6c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -108,6 +108,7 @@
         "ISurfaceComposerClient.cpp",
         "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
+        "LayerMetadata.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
@@ -141,8 +142,6 @@
         "libhidltransport",
         "android.hidl.token@1.0-utils",
         "android.hardware.graphics.bufferqueue@1.0",
-        "android.hardware.configstore@1.0",
-        "android.hardware.configstore-utils",
     ],
 
     // bufferhub is not used when building libgui for vendors
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ad2dc14..bef68ef 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -395,6 +395,32 @@
         return result;
     }
 
+    virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& primaries) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to transact: %d", result);
+            return result;
+        }
+        result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)),
+                    sizeof(ui::DisplayPrimaries));
+        }
+        return result;
+    }
+
     virtual ColorMode getActiveColorMode(const sp<IBinder>& display) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -687,13 +713,13 @@
     virtual status_t getProtectedContentSupport(bool* outSupported) const {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
-        bool result;
-        status_t err = reply.readBool(&result);
-        if (err == NO_ERROR) {
-            *outSupported = result;
+        status_t error =
+                remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
         }
-        return err;
+        error = reply.readBool(outSupported);
+        return error;
     }
 
     virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
@@ -731,6 +757,26 @@
 
         return remote()->transact(BnSurfaceComposer::UNCACHE_BUFFER, data, &reply);
     }
+
+    virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+                                        bool* outIsWideColorDisplay) const {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = data.writeStrongBinder(token);
+        if (error != NO_ERROR) {
+            return error;
+        }
+
+        error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = reply.readBool(outIsWideColorDisplay);
+        return error;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -954,6 +1000,26 @@
             }
             return NO_ERROR;
         }
+        case GET_DISPLAY_NATIVE_PRIMARIES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            ui::DisplayPrimaries primaries;
+            sp<IBinder> display = nullptr;
+
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result);
+                return result;
+            }
+
+            result = getDisplayNativePrimaries(display, primaries);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries,
+                        sizeof(ui::DisplayPrimaries));
+            }
+
+            return NO_ERROR;
+        }
         case GET_ACTIVE_COLOR_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
@@ -1214,6 +1280,20 @@
 
             return uncacheBuffer(token, bufferId);
         }
+        case IS_WIDE_COLOR_DISPLAY: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t error = data.readStrongBinder(&display);
+            if (error != NO_ERROR) {
+                return error;
+            }
+            bool result;
+            error = isWideColorDisplay(display, &result);
+            if (error == NO_ERROR) {
+                reply->writeBool(result);
+            }
+            return error;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 928ef95..129558b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -47,26 +47,26 @@
     ~BpSurfaceComposerClient() override;
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
-                           uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
-                           int32_t ownerUid, sp<IBinder>* handle,
-                           sp<IGraphicBufferProducer>* gbp) override {
+                           uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
+                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
-                                                                            windowType, ownerUid,
+                                                                            std::move(metadata),
                                                                             handle, gbp);
     }
 
     status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
                                      PixelFormat format, uint32_t flags,
-                                     const sp<IGraphicBufferProducer>& parent, int32_t windowType,
-                                     int32_t ownerUid, sp<IBinder>* handle,
+                                     const sp<IGraphicBufferProducer>& parent,
+                                     LayerMetadata metadata, sp<IBinder>* handle,
                                      sp<IGraphicBufferProducer>* gbp) override {
         return callRemote<decltype(
                 &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
                                                                    name, width, height, format,
-                                                                   flags, parent, windowType,
-                                                                   ownerUid, handle, gbp);
+                                                                   flags, parent,
+                                                                   std::move(metadata), handle,
+                                                                   gbp);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
new file mode 100644
index 0000000..745433a
--- /dev/null
+++ b/libs/gui/LayerMetadata.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <gui/LayerMetadata.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+LayerMetadata::LayerMetadata() = default;
+
+LayerMetadata::LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map)
+      : mMap(std::move(map)) {}
+
+LayerMetadata::LayerMetadata(const LayerMetadata& other) = default;
+
+LayerMetadata::LayerMetadata(LayerMetadata&& other) = default;
+
+void LayerMetadata::merge(const LayerMetadata& other) {
+    for (const auto& entry : other.mMap) {
+        mMap[entry.first] = entry.second;
+    }
+}
+
+status_t LayerMetadata::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(static_cast<int>(mMap.size()));
+    status_t status = OK;
+    for (const auto& entry : mMap) {
+        status = parcel->writeUint32(entry.first);
+        if (status != OK) {
+            break;
+        }
+        status = parcel->writeByteVector(entry.second);
+        if (status != OK) {
+            break;
+        }
+    }
+    return status;
+}
+
+status_t LayerMetadata::readFromParcel(const Parcel* parcel) {
+    int size = parcel->readInt32();
+    status_t status = OK;
+    mMap.clear();
+    for (int i = 0; i < size; ++i) {
+        uint32_t key = parcel->readUint32();
+        status = parcel->readByteVector(&mMap[key]);
+        if (status != OK) {
+            break;
+        }
+    }
+    return status;
+}
+
+LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) {
+    mMap = other.mMap;
+    return *this;
+}
+
+LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) {
+    mMap = std::move(other.mMap);
+    return *this;
+}
+
+bool LayerMetadata::has(uint32_t key) const {
+    return mMap.count(key);
+}
+
+int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const {
+    if (!has(key)) return fallback;
+    const std::vector<uint8_t>& data = mMap.at(key);
+    if (data.size() < sizeof(uint32_t)) return fallback;
+    Parcel p;
+    p.setData(data.data(), data.size());
+    return p.readInt32();
+}
+
+void LayerMetadata::setInt32(uint32_t key, int32_t value) {
+    std::vector<uint8_t>& data = mMap[key];
+    Parcel p;
+    p.writeInt32(value);
+    data.resize(p.dataSize());
+    memcpy(data.data(), p.data(), p.dataSize());
+}
+
+std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
+    if (!has(key)) return std::string();
+    switch (key) {
+        case METADATA_OWNER_UID:
+            return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
+        case METADATA_WINDOW_TYPE:
+            return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
+        case METADATA_TASK_ID:
+            return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+        default:
+            return StringPrintf("%d%s%dbytes", key, separator,
+                                static_cast<int>(mMap.at(key).size()));
+    }
+}
+
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index ab92973..6091d3f 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -96,6 +96,7 @@
 
     output.writeStrongBinder(cachedBuffer.token);
     output.writeInt32(cachedBuffer.bufferId);
+    output.writeParcelable(metadata);
 
     output.writeFloat(colorAlpha);
     output.writeUint32(static_cast<uint32_t>(colorDataspace));
@@ -172,6 +173,7 @@
 
     cachedBuffer.token = input.readStrongBinder();
     cachedBuffer.bufferId = input.readInt32();
+    input.readParcelable(&metadata);
 
     colorAlpha = input.readFloat();
     colorDataspace = static_cast<ui::Dataspace>(input.readUint32());
@@ -396,6 +398,10 @@
         what |= eColorDataspaceChanged;
         colorDataspace = other.colorDataspace;
     }
+    if (other.what & eMetadataChanged) {
+        what |= eMetadataChanged;
+        metadata.merge(other.metadata);
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 00e23f0..1f726b2 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -39,9 +39,6 @@
 #include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
 namespace android {
 
 using ui::ColorMode;
@@ -321,41 +318,14 @@
     return NO_ERROR;
 }
 
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
 status_t Surface::getWideColorSupport(bool* supported) {
     ATRACE_CALL();
 
     sp<IBinder> display(
         composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
-    Vector<ColorMode> colorModes;
-    status_t err =
-        composerService()->getDisplayColorModes(display, &colorModes);
-
-    if (err)
-        return err;
-
-    bool wideColorBoardConfig =
-        getBool<ISurfaceFlingerConfigs,
-                &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
-
     *supported = false;
-    for (ColorMode colorMode : colorModes) {
-        switch (colorMode) {
-            case ColorMode::DISPLAY_P3:
-            case ColorMode::ADOBE_RGB:
-            case ColorMode::DCI_P3:
-                if (wideColorBoardConfig) {
-                    *supported = true;
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    return NO_ERROR;
+    status_t error = composerService()->isWideColorDisplay(display, supported);
+    return error;
 }
 
 status_t Surface::getHdrSupport(bool* supported) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6c1c52e..c712bde 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -539,6 +539,20 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata(
+        const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eMetadataChanged;
+    s->metadata.mMap[key] = std::move(data);
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatrix(
         const sp<SurfaceControl>& sc, float dsdx, float dtdx,
         float dtdy, float dsdy) {
@@ -1141,26 +1155,19 @@
     mStatus = NO_INIT;
 }
 
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
-        const String8& name,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        uint32_t flags,
-        SurfaceControl* parent,
-        int32_t windowType,
-        int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
+                                                        PixelFormat format, uint32_t flags,
+                                                        SurfaceControl* parent,
+                                                        LayerMetadata metadata) {
     sp<SurfaceControl> s;
-    createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid);
+    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
     return s;
 }
 
 sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
                                                                   uint32_t h, PixelFormat format,
                                                                   uint32_t flags, Surface* parent,
-                                                                  int32_t windowType,
-                                                                  int32_t ownerUid) {
+                                                                  LayerMetadata metadata) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1169,8 +1176,8 @@
         sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
         sp<IGraphicBufferProducer> gbp;
 
-        err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, windowType,
-                                               ownerUid, &handle, &gbp);
+        err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
+                                               std::move(metadata), &handle, &gbp);
         ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
         if (err == NO_ERROR) {
             return new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -1179,17 +1186,11 @@
     return nullptr;
 }
 
-status_t SurfaceComposerClient::createSurfaceChecked(
-        const String8& name,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        sp<SurfaceControl>* outSurface,
-        uint32_t flags,
-        SurfaceControl* parent,
-        int32_t windowType,
-        int32_t ownerUid)
-{
+status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
+                                                     PixelFormat format,
+                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
+                                                     SurfaceControl* parent,
+                                                     LayerMetadata metadata) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1201,8 +1202,9 @@
         if (parent != nullptr) {
             parentHandle = parent->getHandle();
         }
-        err = mClient->createSurface(name, w, h, format, flags, parentHandle,
-                windowType, ownerUid, &handle, &gbp);
+
+        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
+                                     &handle, &gbp);
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
             *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -1295,6 +1297,11 @@
     return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
 }
 
+status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display,
+        ui::DisplayPrimaries& outPrimaries) {
+    return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries);
+}
+
 ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
     return ComposerService::getComposerService()->getActiveColorMode(display);
 }
@@ -1361,6 +1368,12 @@
                                                                             timestamp, outStats);
 }
 
+status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display,
+                                                   bool* outIsWideColorDisplay) {
+    return ComposerService::getComposerService()->isWideColorDisplay(display,
+                                                                     outIsWideColorDisplay);
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 418d5fb..3899f6a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,6 +27,7 @@
 
 #include <binder/IInterface.h>
 
+#include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
@@ -161,6 +162,8 @@
 
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes) = 0;
+    virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& primaries) = 0;
     virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0;
     virtual status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode) = 0;
@@ -321,6 +324,13 @@
                                  int32_t* outBufferId) = 0;
 
     virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) = 0;
+
+    /*
+     * Queries whether the given display is a wide color display.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+                                        bool* outIsWideColorDisplay) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -365,6 +375,9 @@
         GET_PROTECTED_CONTENT_SUPPORT,
         CACHE_BUFFER,
         UNCACHE_BUFFER,
+        IS_WIDE_COLOR_DISPLAY,
+        GET_DISPLAY_NATIVE_PRIMARIES,
+        // Always append new enum to the end.
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index f443df8..32ac9e8 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -18,8 +18,11 @@
 
 #include <binder/IInterface.h>
 #include <binder/SafeInterface.h>
+#include <gui/LayerMetadata.h>
 #include <ui/PixelFormat.h>
 
+#include <unordered_map>
+
 namespace android {
 
 class FrameStats;
@@ -51,8 +54,8 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
-                                   uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
-                                   int32_t ownerUid, sp<IBinder>* handle,
+                                   uint32_t flags, const sp<IBinder>& parent,
+                                   LayerMetadata metadata, sp<IBinder>* handle,
                                    sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
@@ -61,8 +64,7 @@
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
-                                             int32_t windowType, int32_t ownerUid,
-                                             sp<IBinder>* handle,
+                                             LayerMetadata metadata, sp<IBinder>* handle,
                                              sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
new file mode 100644
index 0000000..3ae10e4
--- /dev/null
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+
+#include <unordered_map>
+
+namespace android {
+
+enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 };
+
+struct LayerMetadata : public Parcelable {
+    std::unordered_map<uint32_t, std::vector<uint8_t>> mMap;
+
+    LayerMetadata();
+    LayerMetadata(const LayerMetadata& other);
+    LayerMetadata(LayerMetadata&& other);
+    explicit LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map);
+    LayerMetadata& operator=(const LayerMetadata& other);
+    LayerMetadata& operator=(LayerMetadata&& other);
+
+    void merge(const LayerMetadata& other);
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    bool has(uint32_t key) const;
+    int32_t getInt32(uint32_t key, int32_t fallback) const;
+    void setInt32(uint32_t key, int32_t value);
+
+    std::string itemToString(uint32_t key, const char* separator) const;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 9063e7f..afd843f 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -30,6 +30,7 @@
 #include <input/InputWindow.h>
 #endif
 
+#include <gui/LayerMetadata.h>
 #include <math/vec3.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
@@ -87,6 +88,7 @@
         eCachedBufferChanged = 0x2'00000000,
         eColorAlphaChanged = 0x4'00000000,
         eColorDataspaceChanged = 0x8'00000000,
+        eMetadataChanged = 0x10'00000000,
     };
 
     layer_state_t()
@@ -187,6 +189,8 @@
 
     float colorAlpha;
     ui::Dataspace colorDataspace;
+
+    LayerMetadata metadata;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 24b656b..8e2bb2b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -30,6 +30,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
@@ -114,6 +115,10 @@
     static status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes);
 
+    // Get the coordinates of the display's native color primaries
+    static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& outPrimaries);
+
     // Gets the active color mode for the given display
     static ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
 
@@ -153,45 +158,42 @@
     // Uncaches a buffer set by cacheBuffer
     static status_t uncacheBuffer(int32_t bufferId);
 
+    // Queries whether a given display is wide color display.
+    static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
     static sp<SurfaceComposerClient> getDefault();
 
     //! Create a surface
-    sp<SurfaceControl> createSurface(
-            const String8& name,// name of the surface
-            uint32_t w,         // width in pixel
-            uint32_t h,         // height in pixel
-            PixelFormat format, // pixel-format desired
-            uint32_t flags = 0, // usage flags
-            SurfaceControl* parent = nullptr, // parent
-            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            int32_t ownerUid = -1 // UID of the task
+    sp<SurfaceControl> createSurface(const String8& name,              // name of the surface
+                                     uint32_t w,                       // width in pixel
+                                     uint32_t h,                       // height in pixel
+                                     PixelFormat format,               // pixel-format desired
+                                     uint32_t flags = 0,               // usage flags
+                                     SurfaceControl* parent = nullptr, // parent
+                                     LayerMetadata metadata = LayerMetadata() // metadata
     );
 
-    status_t createSurfaceChecked(
-            const String8& name, // name of the surface
-            uint32_t w,          // width in pixel
-            uint32_t h,          // height in pixel
-            PixelFormat format,  // pixel-format desired
-            sp<SurfaceControl>* outSurface,
-            uint32_t flags = 0,               // usage flags
-            SurfaceControl* parent = nullptr, // parent
-            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            int32_t ownerUid = -1    // UID of the task
+    status_t createSurfaceChecked(const String8& name, // name of the surface
+                                  uint32_t w,          // width in pixel
+                                  uint32_t h,          // height in pixel
+                                  PixelFormat format,  // pixel-format desired
+                                  sp<SurfaceControl>* outSurface,
+                                  uint32_t flags = 0,                      // usage flags
+                                  SurfaceControl* parent = nullptr,        // parent
+                                  LayerMetadata metadata = LayerMetadata() // metadata
     );
 
     //! Create a surface
-    sp<SurfaceControl> createWithSurfaceParent(
-            const String8& name,       // name of the surface
-            uint32_t w,                // width in pixel
-            uint32_t h,                // height in pixel
-            PixelFormat format,        // pixel-format desired
-            uint32_t flags = 0,        // usage flags
-            Surface* parent = nullptr, // parent
-            int32_t windowType = -1,   // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            int32_t ownerUid = -1      // UID of the task
+    sp<SurfaceControl> createWithSurfaceParent(const String8& name,       // name of the surface
+                                               uint32_t w,                // width in pixel
+                                               uint32_t h,                // height in pixel
+                                               PixelFormat format,        // pixel-format desired
+                                               uint32_t flags = 0,        // usage flags
+                                               Surface* parent = nullptr, // parent
+                                               LayerMetadata metadata = LayerMetadata() // metadata
     );
 
     //! Create a virtual display
@@ -302,6 +304,8 @@
         Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+        Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key,
+                                 std::vector<uint8_t> data);
         // Defers applying any changes made in this transaction until the Layer
         // identified by handle reaches the given frameNumber. If the Layer identified
         // by handle is removed, then we will apply this transaction regardless of
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 259ef9f..1705fd7 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -598,6 +598,10 @@
             Vector<ColorMode>* /*outColorModes*/) override {
         return NO_ERROR;
     }
+    status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/,
+            ui::DisplayPrimaries& /*primaries*/) override {
+        return NO_ERROR;
+    }
     ColorMode getActiveColorMode(const sp<IBinder>& /*display*/)
             override {
         return ColorMode::NATIVE;
@@ -665,6 +669,7 @@
         return NO_ERROR;
     }
     status_t uncacheBuffer(const sp<IBinder>& /*token*/, int32_t /*bufferId*/) { return NO_ERROR; }
+    status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
 
 protected:
     IBinder* onAsBinder() override { return nullptr; }
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/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index c5a9942..c137394 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -449,6 +449,7 @@
 }
 
 base::unique_fd GLESRenderEngine::flush() {
+    ATRACE_CALL();
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
     }
@@ -479,6 +480,7 @@
 }
 
 bool GLESRenderEngine::finish() {
+    ATRACE_CALL();
     if (!GLExtensions::getInstance().hasFenceSync()) {
         ALOGW("no synchronization support");
         return false;
@@ -594,6 +596,7 @@
 }
 
 void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+    ATRACE_CALL();
     const GLImage& glImage = static_cast<const GLImage&>(image);
     const GLenum target = GL_TEXTURE_EXTERNAL_OES;
 
@@ -608,8 +611,15 @@
 }
 
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+                                                     sp<Fence> bufferFence, bool readCache) {
+    return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache,
+                                     /*persistCache=*/false);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
                                                      sp<Fence> bufferFence, bool readCache,
                                                      bool persistCache) {
+    ATRACE_CALL();
     if (readCache) {
         auto cachedImage = mImageCache.find(buffer->getId());
 
@@ -655,7 +665,7 @@
     }
 
     // We don't always want to persist to the cache, e.g. on older devices we
-    // might bind for synchronization purpoeses, but that might leak if we never
+    // might bind for synchronization purposes, but that might leak if we never
     // call drawLayers again, so it's just better to recreate the image again
     // if needed when we draw.
     if (persistCache) {
@@ -703,6 +713,7 @@
 }
 
 status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+    ATRACE_CALL();
     GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
     EGLImageKHR eglImage = glFramebuffer->getEGLImage();
     uint32_t textureName = glFramebuffer->getTextureName();
@@ -770,6 +781,7 @@
                                       const std::vector<LayerSettings>& layers,
                                       ANativeWindowBuffer* const buffer,
                                       base::unique_fd* drawFence) {
+    ATRACE_CALL();
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
         return NO_ERROR;
@@ -786,6 +798,13 @@
 
     evictImages(layers);
 
+    // clear the entire buffer, sometimes when we reuse buffers we'd persist
+    // ghost images otherwise.
+    // we also require a full transparent framebuffer for overlays. This is
+    // probably not quite efficient on all GPUs, since we could filter out
+    // opaque layers.
+    clearWithColor(0.0, 0.0, 0.0, 0.0);
+
     setViewportAndProjection(display.physicalDisplay, display.clip);
 
     setOutputDataSpace(display.outputDataspace);
@@ -794,6 +813,7 @@
     mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
     mState.projectionMatrix = projectionMatrix;
     if (!display.clearRegion.isEmpty()) {
+        glDisable(GL_BLEND);
         fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
     }
 
@@ -813,9 +833,11 @@
 
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
+        bool isOpaque = false;
 
         if (layer.source.buffer.buffer != nullptr) {
             disableTexture = false;
+            isOpaque = layer.source.buffer.isOpaque;
 
             sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
 
@@ -825,17 +847,19 @@
 
             usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
             Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
-            texture.setMatrix(layer.source.buffer.textureTransform.asArray());
+            mat4 texMatrix = layer.source.buffer.textureTransform;
+
+            texture.setMatrix(texMatrix.asArray());
             texture.setFiltering(layer.source.buffer.useTextureFiltering);
 
             texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
             setSourceY410BT2020(layer.source.buffer.isY410BT2020);
 
             renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
-            texCoords[0] = vec2(0.0, 1.0);
-            texCoords[1] = vec2(0.0, 0.0);
-            texCoords[2] = vec2(1.0, 0.0);
-            texCoords[3] = vec2(1.0, 1.0);
+            texCoords[0] = vec2(0.0, 0.0);
+            texCoords[1] = vec2(0.0, 1.0);
+            texCoords[2] = vec2(1.0, 1.0);
+            texCoords[3] = vec2(1.0, 0.0);
             setupLayerTexturing(texture);
         }
 
@@ -843,8 +867,11 @@
         const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
         // Buffer sources will have a black solid color ignored in the shader,
         // so in that scenario the solid color passed here is arbitrary.
-        setupLayerBlending(usePremultipliedAlpha, layer.source.buffer.isOpaque, disableTexture,
-                           color, layer.geometry.roundedCornersRadius);
+        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+                           layer.geometry.roundedCornersRadius);
+        if (layer.disableBlending) {
+            glDisable(GL_BLEND);
+        }
         setSourceDataSpace(layer.sourceDataspace);
 
         drawMesh(mesh);
@@ -903,6 +930,7 @@
 }
 
 void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
+    ATRACE_CALL();
     mVpWidth = viewport.getWidth();
     mVpHeight = viewport.getHeight();
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e094860..34187f1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,6 +71,8 @@
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+                                       bool readCache);
     status_t bindFrameBuffer(Framebuffer* framebuffer) override;
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
@@ -184,6 +186,9 @@
     const bool mUseColorManagement = false;
 
     // Cache of GL images that we'll store per GraphicBuffer ID
+    // TODO: Layer should call back on destruction instead to clean this up,
+    // as it has better system utilization at the potential expense of a
+    // more complicated interface.
     std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
 
     class FlushTracer {
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 4a519bb..0e3b405 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "GLFramebuffer.h"
 
 #include <GLES/gl.h>
@@ -21,6 +23,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <nativebase/nativebase.h>
+#include <utils/Trace.h>
 #include "GLESRenderEngine.h"
 
 namespace android {
@@ -40,6 +43,7 @@
 }
 
 bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
+    ATRACE_CALL();
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         eglDestroyImageKHR(mEGLDisplay, mEGLImage);
         mEGLImage = EGL_NO_IMAGE_KHR;
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index 587cb31..77e648e 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "GLImage.h"
 
 #include <vector>
 
 #include <log/log.h>
+#include <utils/Trace.h>
 #include "GLESRenderEngine.h"
 #include "GLExtensions.h"
 
@@ -50,6 +53,7 @@
 }
 
 bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+    ATRACE_CALL();
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
             ALOGE("failed to destroy image: %#x", eglGetError());
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 56ac714..aa45ed8 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -126,6 +126,9 @@
     // Additional layer-specific color transform to be applied before the global
     // transform.
     mat4 colorTransform = mat4();
+
+    // True if blending will be forced to be disabled.
+    bool disableBlending = false;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 20dd996..bc1a4da 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -108,6 +108,8 @@
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
     virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+    virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+                                               sp<Fence> fence, bool cleanCache) = 0;
     // When binding a native buffer, it must be done before setViewportAndProjection
     // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
     virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index b4c7c96..4b86cfe 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -52,6 +52,7 @@
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
     MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
+    MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool));
     MOCK_CONST_METHOD0(checkErrors, void());
     MOCK_METHOD4(setViewportAndProjection,
                  void(size_t, size_t, Rect, ui::Transform::orientation_flags));
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index bef25a8..0ee6153 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -194,7 +194,7 @@
 
     void clearLeftRegion();
 
-    void fillBufferThenClearRegion();
+    void clearRegion();
 
     // Dumb hack to get aroud the fact that tear-down for renderengine isn't
     // well defined right now, so we can't create multiple instances
@@ -685,14 +685,13 @@
     invokeDraw(settings, layers, mBuffer);
 }
 
-void RenderEngineTest::fillBufferThenClearRegion() {
-    fillGreenBuffer<ColorSourceVariant>();
+void RenderEngineTest::clearRegion() {
     // Reuse mBuffer
     clearLeftRegion();
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
                            DEFAULT_DISPLAY_HEIGHT),
-                      0, 255, 0, 255);
+                      0, 0, 0, 0);
 }
 
 TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
@@ -855,8 +854,8 @@
     fillBufferWithoutPremultiplyAlpha();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferThenClearRegion) {
-    fillBufferThenClearRegion();
+TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+    clearRegion();
 }
 
 } // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 74450d7..00e227f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -87,7 +87,7 @@
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
         "android.hardware.graphics.mapper@3.0",
-        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.2",
         "android.hardware.configstore-utils",
         "libbase",
         "libcutils",
@@ -102,6 +102,7 @@
     ],
 
     export_shared_lib_headers: [
+        "android.hardware.configstore@1.2",
         "android.hardware.graphics.common@1.2",
     ],
 
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 6310f29..4b20772 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -167,30 +167,35 @@
         return -EINVAL;
     }
 
-    int bufferId = bufferTraits.bufferInfo->data[1];
+    // 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[2], sizeof(clientBitMask));
+    memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
     if (clientBitMask == 0U) {
-        ALOGE("%s: Received a invalid client state mask!", __FUNCTION__);
-        return -EINVAL;
-    }
-
-    // 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;
     }
 
     uint32_t userMetadataSize;
-    memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[3], sizeof(userMetadataSize));
+    memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize));
     if (mMetadata.user_metadata_size() != userMetadataSize) {
         ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__,
               userMetadataSize, mMetadata.user_metadata_size());
@@ -313,10 +318,14 @@
     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 {
-    // TODO(b/68770788): check eventFd once implemented
     return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
-            mMetadata.IsValid() && mBufferClient != nullptr;
+            mEventFd.get() >= 0 && mMetadata.IsValid() && mBufferClient != nullptr;
 }
 
 native_handle_t* BufferHubBuffer::Duplicate() {
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp
index 978b352..bffc2ca 100644
--- a/libs/ui/BufferHubEventFd.cpp
+++ b/libs/ui/BufferHubEventFd.cpp
@@ -23,6 +23,8 @@
 
 BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {}
 
+BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {}
+
 status_t BufferHubEventFd::signal() const {
     if (!isValid()) {
         ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__);
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index c6a4a23..0b6d75a 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -21,6 +21,7 @@
 #include <android/hardware_buffer.h>
 #include <cutils/native_handle.h>
 #include <ui/BufferHubDefs.h>
+#include <ui/BufferHubEventFd.h>
 #include <ui/BufferHubMetadata.h>
 
 namespace android {
@@ -55,10 +56,10 @@
         return native_handle_clone(mBufferHandle.getNativeHandle());
     }
 
+    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.
@@ -94,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
@@ -123,6 +127,9 @@
     // Wraps the gralloc buffer handle of this buffer.
     hardware::hidl_handle mBufferHandle;
 
+    // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer.
+    BufferHubEventFd mEventFd;
+
     // An ashmem-based metadata object. The same shared memory are mapped to the
     // bufferhubd daemon and all buffer clients.
     BufferHubMetadata mMetadata;
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h
index 069f0dc..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;
@@ -170,15 +165,16 @@
  *
  * It's definition should follow the following format:
  * {
- *   NumFds = 1,
+ *   NumFds = 2,
  *   NumInts = 3,
  *   data[0] = Ashmem fd for BufferHubMetadata,
- *   data[1] = buffer id,
- *   data[2] = client state bit mask,
- *   data[3] = user metadata size,
+ *   data[1] = event fd,
+ *   data[2] = buffer id,
+ *   data[3] = client state bit mask,
+ *   data[4] = user metadata size,
  * }
  */
-static constexpr int kBufferInfoNumFds = 1;
+static constexpr int kBufferInfoNumFds = 2;
 static constexpr int kBufferInfoNumInts = 3;
 
 } // namespace BufferHubDefs
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h
index 0e856bd..8772304 100644
--- a/libs/ui/include/ui/BufferHubEventFd.h
+++ b/libs/ui/include/ui/BufferHubEventFd.h
@@ -30,6 +30,12 @@
     BufferHubEventFd();
 
     /**
+     * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes
+     * ownership.
+     */
+    BufferHubEventFd(int fd);
+
+    /**
      * Returns whether this BufferHubEventFd holds a valid event_fd.
      */
     bool isValid() const { return get() >= 0; }
diff --git a/libs/ui/include/ui/ConfigStoreTypes.h b/libs/ui/include/ui/ConfigStoreTypes.h
new file mode 100644
index 0000000..37a2bd5
--- /dev/null
+++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
+namespace android {
+namespace ui {
+
+using android::hardware::configstore::V1_2::DisplayPrimaries;
+
+}  // namespace ui
+}  // namespace android
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index fb3c5f8..4b03e44 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -25,11 +25,11 @@
 namespace android {
 namespace ui {
 
-using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
 using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
 
 }  // namespace ui
 }  // namespace android
diff --git a/libs/ui/include_vndk/ui/ConfigStoreTypes.h b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
new file mode 100644
index 0000000..37a2bd5
--- /dev/null
+++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
+namespace android {
+namespace ui {
+
+using android::hardware::configstore::V1_2::DisplayPrimaries;
+
+}  // namespace ui
+}  // namespace android
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index cd744cd..3bcd935 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "BufferHubBufferTest"
 
+#include <errno.h>
+#include <sys/epoll.h>
+
 #include <android/hardware_buffer.h>
 #include <cutils/native_handle.h>
 #include <gmock/gmock.h>
@@ -23,6 +26,7 @@
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
 #include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubEventFd.h>
 
 namespace android {
 
@@ -31,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;
@@ -158,8 +161,34 @@
     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) << strerror(errno);
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd2.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd2.signal();
+    eventFd2.clear();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
 }
 
 TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
@@ -189,8 +218,6 @@
     EXPECT_THAT(b1, IsNull());
 }
 
-// TODO(b/118180214): remove the comment after ag/5856474 landed
-// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
 TEST_F(BufferHubBufferTest, ImportInvalidToken) {
     native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
     token->data[0] = 0;
@@ -202,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.
@@ -291,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);
@@ -329,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);
@@ -346,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/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp
index 92fb33f..2c9aa57 100644
--- a/libs/ui/tests/BufferHubEventFd_test.cpp
+++ b/libs/ui/tests/BufferHubEventFd_test.cpp
@@ -91,22 +91,21 @@
     // Technically, the dupliated eventFd and the original eventFd are pointing
     // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl
     // eventFd.
-    base::unique_fd dupedEventFd(dup(eventFd.get()));
+    BufferHubEventFd dupedEventFd(dup(eventFd.get()));
     ASSERT_GE(dupedEventFd.get(), 0);
 
     std::array<epoll_event, 1> events;
     EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
 
-    eventfd_write(dupedEventFd.get(), 1);
+    dupedEventFd.signal();
     EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
 
     // The epoll fd is edge triggered, so it only responds to the eventFd once.
     EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
 
-    eventfd_write(dupedEventFd.get(), 1);
+    dupedEventFd.signal();
 
-    eventfd_t value;
-    eventfd_read(dupedEventFd.get(), &value);
+    dupedEventFd.clear();
     EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
 }
 
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/vibrator/Android.bp b/libs/vibrator/Android.bp
new file mode 100644
index 0000000..e95a080
--- /dev/null
+++ b/libs/vibrator/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2018 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_library_shared {
+    name: "libvibrator",
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+    ],
+
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+        local_include_dirs: ["include/"],
+        export_aidl_headers: true,
+    },
+
+    srcs: [
+        ":libvibrator_aidl",
+        "*.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wno-unused-parameter",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
new file mode 100644
index 0000000..f6fc19e
--- /dev/null
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vibrator/ExternalVibration.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) {
+    out->writeInt32(attrs.usage);
+    out->writeInt32(attrs.content_type);
+    out->writeInt32(attrs.source);
+    out->writeInt32(attrs.flags);
+}
+
+void readAudioAttributes(audio_attributes_t* attrs, const android::Parcel* in) {
+    attrs->usage = static_cast<audio_usage_t>(in->readInt32());
+    attrs->content_type = static_cast<audio_content_type_t>(in->readInt32());
+    attrs->source = static_cast<audio_source_t>(in->readInt32());
+    attrs->flags = static_cast<audio_flags_mask_t>(in->readInt32());
+}
+
+namespace android {
+namespace os {
+
+ExternalVibration::ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+            sp<IExternalVibrationController> controller) :
+    mUid(uid), mPkg(pkg), mAttrs(attrs), mController(controller) { }
+
+status_t ExternalVibration::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mUid);
+    parcel->writeString16(String16(mPkg.c_str()));
+    writeAudioAttributes(mAttrs, parcel);
+    parcel->writeStrongBinder(IInterface::asBinder(mController));
+    parcel->writeStrongBinder(mToken);
+    return OK;
+}
+status_t ExternalVibration::readFromParcel(const Parcel* parcel) {
+    mUid = parcel->readInt32();
+    String8 pkgStr8 = String8(parcel->readString16());
+    mPkg = pkgStr8.c_str();
+    readAudioAttributes(&mAttrs, parcel);
+    mController = IExternalVibrationController::asInterface(parcel->readStrongBinder());
+    mToken = parcel->readStrongBinder();
+    return OK;
+}
+
+inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const {
+    return mToken == rhs.mToken;
+}
+
+} // namespace os
+} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
new file mode 100644
index 0000000..c6eb3d1
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EXTERNAL_VIBRATION_H
+#define ANDROID_EXTERNAL_VIBRATION_H
+
+#include <android/os/IExternalVibrationController.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
+#include <system/audio.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+class ExternalVibration : public Parcelable, public virtual RefBase {
+public :
+    ExternalVibration() = default;
+    ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+            sp<IExternalVibrationController> controller);
+    virtual ~ExternalVibration() = default;
+    ExternalVibration(const ExternalVibration&) = default;
+
+    bool operator==(const ExternalVibration&) const;
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    int32_t getUid() const { return mUid; }
+    std::string getPackage() const { return mPkg; }
+    audio_attributes_t getAudioAttributes() const { return mAttrs; }
+    sp<IExternalVibrationController> getController() { return mController; }
+
+
+private:
+    int32_t mUid;
+    std::string mPkg;
+    audio_attributes_t mAttrs;
+    sp<IExternalVibrationController> mController;
+    sp<IBinder> mToken = new BBinder();
+};
+
+} // namespace android
+} // namespace os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_H
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/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index ccefe2f..6b802fb 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -65,15 +65,20 @@
     std::lock_guard<std::mutex> lock(mClientSetMutex);
     mClientSet.emplace(client);
 
+    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                                  BufferHubDefs::kBufferInfoNumInts);
     hidl_handle bufferInfo =
-            buildBufferInfo(node->id(), node->AddNewActiveClientsBitToMask(),
-                            node->user_metadata_size(), node->metadata().ashmem_fd());
+            buildBufferInfo(bufferInfoStorage, node->id(), node->AddNewActiveClientsBitToMask(),
+                            node->user_metadata_size(), node->metadata().ashmem_fd(),
+                            node->eventFd().get());
     BufferTraits bufferTraits = {/*bufferDesc=*/description,
                                  /*bufferHandle=*/hidl_handle(node->buffer_handle()),
-                                 /*bufferInfo=*/bufferInfo};
+                                 /*bufferInfo=*/std::move(bufferInfo)};
 
     _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
-             /*bufferTraits=*/bufferTraits);
+             /*bufferTraits=*/std::move(bufferTraits));
     return Void();
 }
 
@@ -154,15 +159,19 @@
     HardwareBufferDescription bufferDesc;
     memcpy(&bufferDesc, &node->buffer_desc(), sizeof(HardwareBufferDescription));
 
-    hidl_handle bufferInfo =
-            buildBufferInfo(node->id(), clientStateMask, node->user_metadata_size(),
-                            node->metadata().ashmem_fd());
+    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                                  BufferHubDefs::kBufferInfoNumInts);
+    hidl_handle bufferInfo = buildBufferInfo(bufferInfoStorage, node->id(), clientStateMask,
+                                             node->user_metadata_size(),
+                                             node->metadata().ashmem_fd(), node->eventFd().get());
     BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
                                  /*bufferHandle=*/hidl_handle(node->buffer_handle()),
-                                 /*bufferInfo=*/bufferInfo};
+                                 /*bufferInfo=*/std::move(bufferInfo)};
 
     _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
-             /*bufferTraits=*/bufferTraits);
+             /*bufferTraits=*/std::move(bufferTraits));
     return Void();
 }
 
@@ -344,20 +353,23 @@
 
 // Implementation of this function should be consistent with the definition of bufferInfo handle in
 // ui/BufferHubDefs.h.
-hidl_handle BufferHubService::buildBufferInfo(int bufferId, uint32_t clientBitMask,
-                                              uint32_t userMetadataSize, const int metadataFd) {
-    native_handle_t* infoHandle = native_handle_create(BufferHubDefs::kBufferInfoNumFds,
-                                                       BufferHubDefs::kBufferInfoNumInts);
+hidl_handle BufferHubService::buildBufferInfo(char* bufferInfoStorage, int bufferId,
+                                              uint32_t clientBitMask, uint32_t userMetadataSize,
+                                              int metadataFd, int eventFd) {
+    native_handle_t* infoHandle =
+            native_handle_init(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                               BufferHubDefs::kBufferInfoNumInts);
 
-    infoHandle->data[0] = dup(metadataFd);
-    infoHandle->data[1] = bufferId;
+    infoHandle->data[0] = metadataFd;
+    infoHandle->data[1] = eventFd;
+    infoHandle->data[2] = bufferId;
     // Use memcpy to convert to int without missing digit.
     // TOOD(b/121345852): use bit_cast to unpack bufferInfo when C++20 becomes available.
-    memcpy(&infoHandle->data[2], &clientBitMask, sizeof(clientBitMask));
-    memcpy(&infoHandle->data[3], &userMetadataSize, sizeof(userMetadataSize));
+    memcpy(&infoHandle->data[3], &clientBitMask, sizeof(clientBitMask));
+    memcpy(&infoHandle->data[4], &userMetadataSize, sizeof(userMetadataSize));
 
     hidl_handle bufferInfo;
-    bufferInfo.setTo(infoHandle, /*shouldOwn=*/true);
+    bufferInfo.setTo(infoHandle, /*shouldOwn=*/false);
 
     return bufferInfo;
 }
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 23e664e..edad20b 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -58,8 +58,8 @@
 
 private:
     // Helper function to build BufferTraits.bufferInfo handle
-    hidl_handle buildBufferInfo(int bufferId, uint32_t clientBitMask, uint32_t userMetadataSize,
-                                const int metadataFd);
+    hidl_handle buildBufferInfo(char* bufferInfoStorage, int bufferId, uint32_t clientBitMask,
+                                uint32_t userMetadataSize, int metadataFd, int eventFd);
 
     // Helper function to remove all the token belongs to a specific client.
     void removeTokenByClient(const BufferClient* client);
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
index b7a195b..4729e1c 100644
--- a/services/bufferhub/include/bufferhub/BufferNode.h
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -4,6 +4,7 @@
 #include <android/hardware_buffer.h>
 #include <bufferhub/BufferHubIdGenerator.h>
 #include <cutils/native_handle.h>
+#include <ui/BufferHubEventFd.h>
 #include <ui/BufferHubMetadata.h>
 
 namespace android {
@@ -31,6 +32,9 @@
     const native_handle_t* buffer_handle() const { return buffer_handle_; }
     const AHardwareBuffer_Desc& buffer_desc() const { return buffer_desc_; }
 
+    // Accessor of event fd.
+    const BufferHubEventFd& eventFd() const { return mEventFd; }
+
     // Accessors of metadata.
     const BufferHubMetadata& metadata() const { return metadata_; }
 
@@ -60,6 +64,9 @@
     native_handle_t* buffer_handle_;
     AHardwareBuffer_Desc buffer_desc_;
 
+    // Eventfd used for signalling buffer events among the clients of the buffer.
+    BufferHubEventFd mEventFd;
+
     // Metadata in shared memory.
     BufferHubMetadata metadata_;
 
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index e3a237e..f73d498 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -16,21 +16,25 @@
     name: "libinputflinger",
 
     srcs: [
+        "InputClassifier.cpp",
         "InputDispatcher.cpp",
         "InputManager.cpp",
     ],
 
     shared_libs: [
+        "android.hardware.input.classifier@1.0",
         "libinputflinger_base",
         "libinputreporter",
         "libinputreader",
         "libbase",
         "libbinder",
         "libcutils",
+        "libhidlbase",
         "libinput",
         "liblog",
         "libutils",
         "libui",
+        "server_configurable_flags",
     ],
 
     cflags: [
@@ -38,7 +42,9 @@
         "-Wextra",
         "-Werror",
         "-Wno-unused-parameter",
-        // TODO: Move inputflinger to its own process and mark it hidden
+        // TODO(b/123097103): annotate InputDispatcher and uncomment the following line
+        //"-Wthread-safety",
+        // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
     ],
 
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
new file mode 100644
index 0000000..b892120
--- /dev/null
+++ b/services/inputflinger/BlockingQueue.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_BLOCKING_QUEUE_H
+#define _UI_INPUT_BLOCKING_QUEUE_H
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+/**
+ * A FIFO queue that stores up to <i>capacity</i> objects.
+ * Objects can always be added. Objects are added immediately.
+ * If the queue is full, new objects cannot be added.
+ *
+ * The action of retrieving an object will block until an element is available.
+ */
+template <class T>
+class BlockingQueue {
+public:
+    BlockingQueue(size_t capacity) : mCapacity(capacity) {
+        mQueue.reserve(mCapacity);
+    };
+
+    /**
+     * Retrieve and remove the oldest object.
+     * Blocks execution while queue is empty.
+     */
+    T pop() {
+        std::unique_lock<std::mutex> lock(mLock);
+        mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); });
+        T t = std::move(mQueue.front());
+        mQueue.erase(mQueue.begin());
+        return std::move(t);
+    };
+
+    /**
+     * Add a new object to the queue.
+     * Does not block.
+     * Return true if an element was successfully added.
+     * Return false if the queue is full.
+     */
+    bool push(T&& t) {
+        std::unique_lock<std::mutex> lock(mLock);
+        if (mQueue.size() == mCapacity) {
+            return false;
+        }
+        mQueue.push_back(std::move(t));
+        mHasElements.notify_one();
+        return true;
+    };
+
+    void erase(const std::function<bool(const T&)>& lambda) {
+        std::unique_lock<std::mutex> lock(mLock);
+        mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
+                [&lambda](const T& t) { return lambda(t); }), mQueue.end());
+    }
+
+    /**
+     * Remove all elements.
+     * Does not block.
+     */
+    void clear() {
+        std::scoped_lock lock(mLock);
+        mQueue.clear();
+    };
+
+private:
+    size_t mCapacity;
+    /**
+     * Used to signal that mQueue is non-empty.
+     */
+    std::condition_variable mHasElements;
+    /**
+     * Lock for accessing and waiting on elements.
+     */
+    std::mutex mLock;
+    std::vector<T> mQueue; //GUARDED_BY(mLock)
+};
+
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
new file mode 100644
index 0000000..11427c3
--- /dev/null
+++ b/services/inputflinger/InputClassifier.cpp
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputClassifier"
+
+#include "InputClassifier.h"
+
+#include <algorithm>
+#include <cmath>
+#include <inttypes.h>
+#include <log/log.h>
+#if defined(__linux__)
+    #include <pthread.h>
+#endif
+#include <server_configurable_flags/get_flags.h>
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using android::hardware::hidl_bitfield;
+using android::hardware::hidl_vec;
+using namespace android::hardware::input;
+
+namespace android {
+
+static constexpr bool DEBUG = false;
+
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+// Feature flag name for the deep press feature
+static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
+
+//Max number of elements to store in mEvents.
+static constexpr size_t MAX_EVENTS = 5;
+
+template<class K, class V>
+static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
+    auto it = map.find(key);
+    if (it == map.end()) {
+        return defaultValue;
+    }
+    return it->second;
+}
+
+static common::V1_0::Source getSource(uint32_t source) {
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_UNKNOWN) ==
+            common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_KEYBOARD) ==
+            common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_DPAD) ==
+            common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_GAMEPAD) ==
+            common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
+            common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE) ==
+            common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_STYLUS) ==
+            common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
+            common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TRACKBALL) ==
+            common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
+            common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHPAD) ==
+            common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
+            common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_JOYSTICK) ==
+            common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
+            common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ANY) ==
+            common::V1_0::Source::ANY, "SOURCE_ANY mismatch");
+    return static_cast<common::V1_0::Source>(source);
+}
+
+static common::V1_0::Action getAction(int32_t actionMasked) {
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_DOWN) ==
+            common::V1_0::Action::DOWN, "ACTION_DOWN mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_UP) ==
+            common::V1_0::Action::UP, "ACTION_UP mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_MOVE) ==
+            common::V1_0::Action::MOVE, "ACTION_MOVE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
+            common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
+            common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
+            common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
+            common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch");
+    static_assert(static_cast<common::V1_0::Action>( AMOTION_EVENT_ACTION_HOVER_MOVE) ==
+            common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
+            common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
+            common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
+            common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
+            common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
+            common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch");
+    return static_cast<common::V1_0::Action>(actionMasked);
+}
+
+static common::V1_0::Button getActionButton(int32_t actionButton) {
+    static_assert(static_cast<common::V1_0::Button>(0) ==
+            common::V1_0::Button::NONE, "BUTTON_NONE mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
+            common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
+            common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
+            common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_BACK) ==
+            common::V1_0::Button::BACK, "BUTTON_BACK mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
+            common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
+            common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
+            common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch");
+    return static_cast<common::V1_0::Button>(actionButton);
+}
+
+static hidl_bitfield<common::V1_0::Flag> getFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
+            common::V1_0::Flag::WINDOW_IS_OBSCURED);
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
+            common::V1_0::Flag::IS_GENERATED_GESTURE);
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_TAINTED) ==
+            common::V1_0::Flag::TAINTED);
+    return static_cast<hidl_bitfield<common::V1_0::Flag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::PolicyFlag> getPolicyFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_WAKE) ==
+            common::V1_0::PolicyFlag::WAKE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
+            common::V1_0::PolicyFlag::VIRTUAL);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
+            common::V1_0::PolicyFlag::FUNCTION);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_GESTURE) ==
+            common::V1_0::PolicyFlag::GESTURE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INJECTED) ==
+            common::V1_0::PolicyFlag::INJECTED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
+            common::V1_0::PolicyFlag::TRUSTED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FILTERED) ==
+            common::V1_0::PolicyFlag::FILTERED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
+            common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
+            common::V1_0::PolicyFlag::INTERACTIVE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
+            common::V1_0::PolicyFlag::PASS_TO_USER);
+    return static_cast<hidl_bitfield<common::V1_0::PolicyFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::EdgeFlag> getEdgeFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
+            common::V1_0::EdgeFlag::NONE);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
+            common::V1_0::EdgeFlag::TOP);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
+            common::V1_0::EdgeFlag::BOTTOM);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
+            common::V1_0::EdgeFlag::LEFT);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
+            common::V1_0::EdgeFlag::RIGHT);
+    return static_cast<hidl_bitfield<common::V1_0::EdgeFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::Meta> getMetastate(int32_t state) {
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_NONE) ==
+            common::V1_0::Meta::NONE);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_ON) ==
+            common::V1_0::Meta::ALT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_LEFT_ON) ==
+            common::V1_0::Meta::ALT_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_RIGHT_ON) ==
+            common::V1_0::Meta::ALT_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_ON) ==
+            common::V1_0::Meta::SHIFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_LEFT_ON) ==
+            common::V1_0::Meta::SHIFT_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_RIGHT_ON) ==
+            common::V1_0::Meta::SHIFT_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SYM_ON) ==
+            common::V1_0::Meta::SYM_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_FUNCTION_ON) ==
+            common::V1_0::Meta::FUNCTION_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_ON) ==
+            common::V1_0::Meta::CTRL_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_LEFT_ON) ==
+            common::V1_0::Meta::CTRL_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_RIGHT_ON) ==
+            common::V1_0::Meta::CTRL_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_ON) ==
+            common::V1_0::Meta::META_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_LEFT_ON) ==
+            common::V1_0::Meta::META_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_RIGHT_ON) ==
+            common::V1_0::Meta::META_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CAPS_LOCK_ON) ==
+            common::V1_0::Meta::CAPS_LOCK_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_NUM_LOCK_ON) ==
+            common::V1_0::Meta::NUM_LOCK_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SCROLL_LOCK_ON) ==
+            common::V1_0::Meta::SCROLL_LOCK_ON);
+    return static_cast<hidl_bitfield<common::V1_0::Meta>>(state);
+}
+
+static hidl_bitfield<common::V1_0::Button> getButtonState(int32_t buttonState) {
+    // No need for static_assert here.
+    // The button values have already been asserted in getActionButton(..) above
+    return static_cast<hidl_bitfield<common::V1_0::Button>>(buttonState);
+}
+
+static common::V1_0::ToolType getToolType(int32_t toolType) {
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
+            common::V1_0::ToolType::UNKNOWN);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
+            common::V1_0::ToolType::FINGER);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
+            common::V1_0::ToolType::STYLUS);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
+            common::V1_0::ToolType::MOUSE);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
+            common::V1_0::ToolType::ERASER);
+    return static_cast<common::V1_0::ToolType>(toolType);
+}
+
+static common::V1_0::Axis getAxis(uint64_t axis) {
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_X) ==
+            common::V1_0::Axis::X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Y) ==
+            common::V1_0::Axis::Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_PRESSURE) ==
+            common::V1_0::Axis::PRESSURE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SIZE) ==
+            common::V1_0::Axis::SIZE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
+            common::V1_0::Axis::TOUCH_MAJOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
+            common::V1_0::Axis::TOUCH_MINOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) ==
+            common::V1_0::Axis::TOOL_MAJOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) ==
+            common::V1_0::Axis::TOOL_MINOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
+            common::V1_0::Axis::ORIENTATION);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_VSCROLL) ==
+            common::V1_0::Axis::VSCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HSCROLL) ==
+            common::V1_0::Axis::HSCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Z) ==
+            common::V1_0::Axis::Z);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RX) ==
+            common::V1_0::Axis::RX);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RY) ==
+            common::V1_0::Axis::RY);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RZ) ==
+            common::V1_0::Axis::RZ);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_X) ==
+            common::V1_0::Axis::HAT_X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_Y) ==
+            common::V1_0::Axis::HAT_Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) ==
+            common::V1_0::Axis::LTRIGGER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) ==
+            common::V1_0::Axis::RTRIGGER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_THROTTLE) ==
+            common::V1_0::Axis::THROTTLE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RUDDER) ==
+            common::V1_0::Axis::RUDDER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_WHEEL) ==
+            common::V1_0::Axis::WHEEL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GAS) ==
+            common::V1_0::Axis::GAS);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_BRAKE) ==
+            common::V1_0::Axis::BRAKE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_DISTANCE) ==
+            common::V1_0::Axis::DISTANCE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TILT) ==
+            common::V1_0::Axis::TILT);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SCROLL) ==
+            common::V1_0::Axis::SCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) ==
+            common::V1_0::Axis::RELATIVE_X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) ==
+            common::V1_0::Axis::RELATIVE_Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) ==
+            common::V1_0::Axis::GENERIC_1);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) ==
+            common::V1_0::Axis::GENERIC_2);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) ==
+            common::V1_0::Axis::GENERIC_3);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) ==
+            common::V1_0::Axis::GENERIC_4);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) ==
+            common::V1_0::Axis::GENERIC_5);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) ==
+            common::V1_0::Axis::GENERIC_6);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) ==
+            common::V1_0::Axis::GENERIC_7);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) ==
+            common::V1_0::Axis::GENERIC_8);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) ==
+            common::V1_0::Axis::GENERIC_9);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) ==
+            common::V1_0::Axis::GENERIC_10);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) ==
+            common::V1_0::Axis::GENERIC_11);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) ==
+            common::V1_0::Axis::GENERIC_12);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) ==
+            common::V1_0::Axis::GENERIC_13);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) ==
+            common::V1_0::Axis::GENERIC_14);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) ==
+            common::V1_0::Axis::GENERIC_15);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) ==
+            common::V1_0::Axis::GENERIC_16);
+    return static_cast<common::V1_0::Axis>(axis);
+}
+
+static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
+    common::V1_0::VideoFrame out;
+    out.width = frame.getWidth();
+    out.height = frame.getHeight();
+    out.data = frame.getData();
+    struct timeval timestamp = frame.getTimestamp();
+    out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
+             microseconds_to_nanoseconds(timestamp.tv_usec);
+    return out;
+}
+
+static std::vector<common::V1_0::VideoFrame> convertVideoFrames(
+        const std::vector<TouchVideoFrame>& frames) {
+    std::vector<common::V1_0::VideoFrame> out;
+    for (const TouchVideoFrame& frame : frames) {
+        out.push_back(getHalVideoFrame(frame));
+    }
+    return out;
+}
+
+static uint8_t getActionIndex(int32_t action) {
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+            AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args,
+        std::vector<common::V1_0::PointerProperties>* outPointerProperties,
+        std::vector<common::V1_0::PointerCoords>* outPointerCoords) {
+    outPointerProperties->reserve(args.pointerCount);
+    outPointerCoords->reserve(args.pointerCount);
+    for (size_t i = 0; i < args.pointerCount; i++) {
+        common::V1_0::PointerProperties properties;
+        properties.id = args.pointerProperties[i].id;
+        properties.toolType = getToolType(args.pointerProperties[i].toolType);
+        outPointerProperties->push_back(properties);
+
+        common::V1_0::PointerCoords coords;
+        BitSet64 bits (args.pointerCoords[i].bits);
+        std::vector<float> values;
+        size_t index = 0;
+        while (!bits.isEmpty()) {
+            uint32_t axis = bits.clearFirstMarkedBit();
+            coords.bits |= 1 << static_cast<uint64_t>(getAxis(axis));
+            float value = args.pointerCoords[i].values[index++];
+            values.push_back(value);
+        }
+        coords.values = values;
+        outPointerCoords->push_back(coords);
+    }
+}
+
+static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) {
+    common::V1_0::MotionEvent event;
+    event.deviceId = args.deviceId;
+    event.source = getSource(args.source);
+    event.displayId = args.displayId;
+    event.downTime = args.downTime;
+    event.eventTime = args.eventTime;
+    event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
+    event.actionIndex = getActionIndex(args.action);
+    event.actionButton = getActionButton(args.actionButton);
+    event.flags = getFlags(args.flags);
+    event.policyFlags = getPolicyFlags(args.policyFlags);
+    event.edgeFlags = getEdgeFlags(args.edgeFlags);
+    event.metaState = getMetastate(args.metaState);
+    event.buttonState = getButtonState(args.buttonState);
+    event.xPrecision = args.xPrecision;
+    event.yPrecision = args.yPrecision;
+
+    std::vector<common::V1_0::PointerProperties> pointerProperties;
+    std::vector<common::V1_0::PointerCoords> pointerCoords;
+    getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords);
+    event.pointerProperties = pointerProperties;
+    event.pointerCoords = pointerCoords;
+
+    event.deviceTimestamp = args.deviceTimestamp;
+    event.frames = convertVideoFrames(args.videoFrames);
+
+    return event;
+}
+
+static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
+    static_assert(MotionClassification::NONE ==
+            static_cast<MotionClassification>(common::V1_0::Classification::NONE));
+    static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
+            static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
+    static_assert(MotionClassification::DEEP_PRESS ==
+            static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
+    return static_cast<MotionClassification>(classification);
+}
+
+static bool isTouchEvent(const NotifyMotionArgs& args) {
+    return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+}
+
+// Check if the "deep touch" feature is on.
+static bool deepPressEnabled() {
+    std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
+            INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
+    std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
+    if (flag_value == "1" || flag_value == "true") {
+        ALOGI("Deep press feature enabled.");
+        return true;
+    }
+    ALOGI("Deep press feature is not enabled.");
+    return false;
+}
+
+
+// --- ClassifierEvent ---
+
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
+        type(ClassifierEventType::MOTION), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
+        type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
+        type(type), args(std::move(args)) { };
+
+ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
+        type(other.type), args(std::move(other.args)) { };
+
+ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
+    type = other.type;
+    args = std::move(other.args);
+    return *this;
+}
+
+ClassifierEvent ClassifierEvent::createHalResetEvent() {
+    return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+}
+
+ClassifierEvent ClassifierEvent::createExitEvent() {
+    return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+}
+
+std::optional<int32_t> ClassifierEvent::getDeviceId() const {
+    switch (type) {
+        case ClassifierEventType::MOTION: {
+            NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
+            return motionArgs->deviceId;
+        }
+        case ClassifierEventType::DEVICE_RESET: {
+            NotifyDeviceResetArgs* deviceResetArgs =
+                    static_cast<NotifyDeviceResetArgs*>(args.get());
+            return deviceResetArgs->deviceId;
+        }
+        case ClassifierEventType::HAL_RESET: {
+            return std::nullopt;
+        }
+        case ClassifierEventType::EXIT: {
+            return std::nullopt;
+        }
+    }
+}
+
+// --- MotionClassifier ---
+
+MotionClassifier::MotionClassifier(
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) :
+        mEvents(MAX_EVENTS), mService(service) {
+    mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+#if defined(__linux__)
+    // Set the thread name for debugging
+    pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+MotionClassifier::~MotionClassifier() {
+    requestExit();
+    mHalThread.join();
+}
+
+void MotionClassifier::ensureHalThread(const char* function) {
+    if (DEBUG) {
+        if (std::this_thread::get_id() != mHalThread.get_id()) {
+            ALOGE("Function %s should only be called from InputClassifier thread", function);
+        }
+    }
+}
+
+/**
+ * Obtain the classification from the HAL for a given MotionEvent.
+ * Should only be called from the InputClassifier thread (mHalThread).
+ * Should not be called from the thread that notifyMotion runs on.
+ *
+ * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
+ * to return a classification, this would directly impact the touch latency.
+ * To remove any possibility of negatively affecting the touch latency, the HAL
+ * is called from a dedicated thread.
+ */
+void MotionClassifier::callInputClassifierHal() {
+    ensureHalThread(__func__);
+    while (true) {
+        ClassifierEvent event = mEvents.pop();
+        switch (event.type) {
+            case ClassifierEventType::MOTION: {
+                NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
+                common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs);
+                common::V1_0::Classification halClassification = mService->classify(motionEvent);
+                updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+                        getMotionClassification(halClassification));
+                break;
+            }
+            case ClassifierEventType::DEVICE_RESET: {
+                const int32_t deviceId = *(event.getDeviceId());
+                mService->resetDevice(deviceId);
+                setClassification(deviceId, MotionClassification::NONE);
+                break;
+            }
+            case ClassifierEventType::HAL_RESET: {
+                mService->reset();
+                clearClassifications();
+                break;
+            }
+            case ClassifierEventType::EXIT: {
+                clearClassifications();
+                return;
+            }
+        }
+    }
+}
+
+void MotionClassifier::requestExit() {
+    reset();
+    mEvents.push(ClassifierEvent::createExitEvent());
+}
+
+void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
+        MotionClassification classification) {
+    std::scoped_lock lock(mLock);
+    const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+    if (eventTime < lastDownTime) {
+        // HAL just finished processing an event that belonged to an earlier gesture,
+        // but new gesture is already in progress. Drop this classification.
+        ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
+                nanoseconds_to_milliseconds(lastDownTime - eventTime));
+        return;
+    }
+    mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
+    std::scoped_lock lock(mLock);
+    mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::clearClassifications() {
+    std::scoped_lock lock(mLock);
+    mClassifications.clear();
+}
+
+MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
+    std::scoped_lock lock(mLock);
+    return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+}
+
+void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
+    std::scoped_lock lock(mLock);
+    mLastDownTimes[deviceId] = downTime;
+    mClassifications[deviceId] = MotionClassification::NONE;
+}
+
+MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
+    if (!mService) {
+        // If HAL is not present, do nothing
+        return MotionClassification::NONE;
+    }
+    if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
+        updateLastDownTime(args.deviceId, args.downTime);
+    }
+
+    ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
+    bool elementAdded = mEvents.push(std::move(event));
+    if (!elementAdded) {
+        // Queue should not ever overfill. Suspect HAL is slow.
+        ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime);
+        reset();
+        return MotionClassification::NONE;
+    }
+    return getClassification(args.deviceId);
+}
+
+void MotionClassifier::reset() {
+    mEvents.clear();
+    mEvents.push(ClassifierEvent::createHalResetEvent());
+}
+
+/**
+ * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
+ * Request InputClassifier thread to call resetDevice for this particular device.
+ */
+void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
+    int32_t deviceId = args.deviceId;
+    // Clear the pending events right away, to avoid unnecessary work done by the HAL.
+    mEvents.erase([deviceId](const ClassifierEvent& event) {
+            std::optional<int32_t> eventDeviceId = event.getDeviceId();
+            return eventDeviceId && (*eventDeviceId == deviceId);
+    });
+    mEvents.push(std::make_unique<NotifyDeviceResetArgs>(args));
+}
+
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
+        mListener(listener) {
+    if (deepPressEnabled()) {
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+                classifier::V1_0::IInputClassifier::getService();
+        if (service) {
+            mMotionClassifier = std::make_unique<MotionClassifier>(service);
+        } else {
+            ALOGI("Could not obtain InputClassifier HAL");
+        }
+    }
+};
+
+void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+    // pass through
+    mListener->notifyConfigurationChanged(args);
+}
+
+void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
+    // pass through
+    mListener->notifyKey(args);
+}
+
+void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+    if (mMotionClassifier && isTouchEvent(*args)) {
+        // We only cover touch events, for now.
+        NotifyMotionArgs newArgs = NotifyMotionArgs(*args);
+        newArgs.classification = mMotionClassifier->classify(newArgs);
+        args = &newArgs;
+    }
+    mListener->notifyMotion(args);
+}
+
+void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
+    // pass through
+    mListener->notifySwitch(args);
+}
+
+void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+    if (mMotionClassifier) {
+        mMotionClassifier->reset(*args);
+    }
+    // continue to next stage
+    mListener->notifyDeviceReset(args);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
new file mode 100644
index 0000000..1f7dd41
--- /dev/null
+++ b/services/inputflinger/InputClassifier.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_CLASSIFIER_H
+#define _UI_INPUT_CLASSIFIER_H
+
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include <thread>
+
+#include "BlockingQueue.h"
+#include "InputListener.h"
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+namespace android {
+
+enum class ClassifierEventType : uint8_t {
+    MOTION = 0,
+    DEVICE_RESET = 1,
+    HAL_RESET = 2,
+    EXIT = 3,
+};
+
+struct ClassifierEvent {
+    ClassifierEventType type;
+    std::unique_ptr<NotifyArgs> args;
+
+    ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
+    ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
+    ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
+    ClassifierEvent(ClassifierEvent&& other);
+    ClassifierEvent& operator=(ClassifierEvent&& other);
+
+    // Convenience function to create a HAL_RESET event
+    static ClassifierEvent createHalResetEvent();
+    // Convenience function to create an EXIT event
+    static ClassifierEvent createExitEvent();
+
+    std::optional<int32_t> getDeviceId() const;
+};
+
+// --- Interfaces ---
+
+/**
+ * Interface for adding a MotionClassification to NotifyMotionArgs.
+ *
+ * To implement, override the classify function.
+ */
+class MotionClassifierInterface {
+public:
+    MotionClassifierInterface() { }
+    virtual ~MotionClassifierInterface() { }
+    /**
+     * Based on the motion event described by NotifyMotionArgs,
+     * provide a MotionClassification for the current gesture.
+     */
+    virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
+    virtual void reset() = 0;
+    virtual void reset(const NotifyDeviceResetArgs& args) = 0;
+};
+
+/**
+ * Base interface for an InputListener stage.
+ * Provides classification to events.
+ */
+class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+protected:
+    InputClassifierInterface() { }
+    virtual ~InputClassifierInterface() { }
+};
+
+// --- Implementations ---
+
+/**
+ * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
+ * in order to determine the classification for the current gesture.
+ *
+ * The InputClassifier HAL may keep track of the entire gesture in order to determine
+ * the classification, and may be hardware-specific. It may use the data in
+ * NotifyMotionArgs::videoFrames field to drive the classification decisions.
+ * The HAL is called from a separate thread.
+ */
+class MotionClassifier final : public MotionClassifierInterface {
+public:
+    MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
+    ~MotionClassifier();
+    /**
+     * Classifies events asynchronously; that is, it doesn't block events on a classification,
+     * but instead sends them over to the classifier HAL
+     * and after a classification is determined,
+     * it then marks the next event it sees in the stream with it.
+     *
+     * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
+     * in a particular gesture.
+     */
+    virtual MotionClassification classify(const NotifyMotionArgs& args) override;
+    virtual void reset() override;
+    virtual void reset(const NotifyDeviceResetArgs& args) override;
+
+private:
+    // The events that need to be sent to the HAL.
+    BlockingQueue<ClassifierEvent> mEvents;
+    /**
+     * Thread that will communicate with InputClassifier HAL.
+     * This should be the only thread that communicates with InputClassifier HAL,
+     * because this thread is allowed to block on the HAL calls.
+     */
+    std::thread mHalThread;
+    /**
+     * Print an error message if the caller is not on the InputClassifier thread.
+     * Caller must supply the name of the calling function as __function__
+     */
+    void ensureHalThread(const char* function);
+    /**
+     * Call the InputClassifier HAL
+     */
+    void callInputClassifierHal();
+    /**
+     * Access to the InputClassifier HAL
+     */
+    sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
+    std::mutex mLock;
+    /**
+     * Per-device input classifications. Should only be accessed using the
+     * getClassification / setClassification methods.
+     */
+    std::unordered_map<int32_t /*deviceId*/, MotionClassification>
+            mClassifications; //GUARDED_BY(mLock);
+    /**
+     * Set the current classification for a given device.
+     */
+    void setClassification(int32_t deviceId, MotionClassification classification);
+    /**
+     * Get the current classification for a given device.
+     */
+    MotionClassification getClassification(int32_t deviceId);
+    void updateClassification(int32_t deviceId, nsecs_t eventTime,
+        MotionClassification classification);
+    /**
+     * Clear all current classifications
+     */
+    void clearClassifications();
+    /**
+     * Per-device times when the last ACTION_DOWN was received.
+     * Used to reject late classifications that do not belong to the current gesture.
+     *
+     * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
+     */
+    std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/>
+            mLastDownTimes; //GUARDED_BY(mLock);
+    void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
+    // Should only be accessed through isResetNeeded() and setResetNeeded()
+    bool mResetNeeded = false; //GUARDED_BY(mLock);
+    /**
+     * Check whether reset should be performed. Reset should be performed
+     * if the eventTime of the current event is older than mLastDownTime,
+     * i.e. a new gesture has already begun, but an older gesture is still being processed.
+     */
+    bool isResetNeeded(nsecs_t eventTime);
+    void setResetNeeded(bool isNeeded);
+
+    /**
+     * Exit the InputClassifier HAL thread.
+     * Useful for tests to ensure proper cleanup.
+     */
+    void requestExit();
+};
+
+/**
+ * Implementation of the InputClassifierInterface.
+ * Represents a separate stage of input processing. All of the input events go through this stage.
+ * Acts as a passthrough for all input events except for motion events.
+ * The events of motion type are sent to MotionClassifier.
+ */
+class InputClassifier : public InputClassifierInterface {
+public:
+    explicit InputClassifier(const sp<InputListenerInterface>& listener);
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    virtual void notifyKey(const NotifyKeyArgs* args);
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifySwitch(const NotifySwitchArgs* args);
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+private:
+    std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr;
+    // The next stage to pass input events to
+    sp<InputListenerInterface> mListener;
+};
+
+} // namespace android
+#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/InputListener.cpp b/services/inputflinger/InputListener.cpp
index b349c73..e537e09 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -36,6 +36,10 @@
         NotifyArgs(other.sequenceNum), eventTime(other.eventTime) {
 }
 
+bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime;
+}
+
 void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyConfigurationChanged(this);
 }
@@ -61,6 +65,21 @@
         metaState(other.metaState), downTime(other.downTime) {
 }
 
+bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId
+            && source == rhs.source
+            && displayId == rhs.displayId
+            && policyFlags == rhs.policyFlags
+            && action == rhs.action
+            && flags == rhs.flags
+            && keyCode == rhs.keyCode
+            && scanCode == rhs.scanCode
+            && metaState == rhs.metaState
+            && downTime == rhs.downTime;
+}
+
 void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyKey(this);
 }
@@ -105,6 +124,43 @@
     }
 }
 
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+    bool equal =
+            sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId
+            && source == rhs.source
+            && displayId == rhs.displayId
+            && policyFlags == rhs.policyFlags
+            && action == rhs.action
+            && actionButton == rhs.actionButton
+            && flags == rhs.flags
+            && metaState == rhs.metaState
+            && buttonState == rhs.buttonState
+            && classification == rhs.classification
+            && edgeFlags == rhs.edgeFlags
+            && deviceTimestamp == rhs.deviceTimestamp
+            && pointerCount == rhs.pointerCount
+            // PointerProperties and PointerCoords are compared separately below
+            && xPrecision == rhs.xPrecision
+            && yPrecision == rhs.yPrecision
+            && downTime == rhs.downTime
+            && videoFrames == rhs.videoFrames;
+    if (!equal) {
+        return false;
+    }
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        equal =
+                pointerProperties[i] == rhs.pointerProperties[i]
+                && pointerCoords[i] == rhs.pointerCoords[i];
+        if (!equal) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyMotion(this);
 }
@@ -123,6 +179,14 @@
         switchValues(other.switchValues), switchMask(other.switchMask) {
 }
 
+bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && policyFlags == rhs.policyFlags
+            && switchValues == rhs.switchValues
+            && switchMask == rhs.switchMask;
+}
+
 void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifySwitch(this);
 }
@@ -139,6 +203,12 @@
         NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId) {
 }
 
+bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId;
+}
+
 void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyDeviceReset(this);
 }
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 1d7ea00..b3b9e3e 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -34,7 +34,8 @@
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
     mDispatcher = new InputDispatcher(dispatcherPolicy);
-    mReader = createInputReader(readerPolicy, mDispatcher);
+    mClassifier = new InputClassifier(mDispatcher);
+    mReader = createInputReader(readerPolicy, mClassifier);
     initialize();
 }
 
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index ab309b1..142ec0c 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -23,7 +23,9 @@
 
 #include "EventHub.h"
 #include "InputReaderBase.h"
+#include "InputClassifier.h"
 #include "InputDispatcher.h"
+#include "InputReader.h"
 
 #include <input/Input.h>
 #include <input/InputTransport.h>
@@ -100,6 +102,8 @@
     sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
 
+    sp<InputClassifierInterface> mClassifier;
+
     sp<InputDispatcherInterface> mDispatcher;
     sp<InputDispatcherThread> mDispatcherThread;
 
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/InputListener.h b/services/inputflinger/include/InputListener.h
index 53247a0..13ae7dd 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -49,6 +49,8 @@
 
     inline NotifyConfigurationChangedArgs() { }
 
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
+
     NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime);
 
     NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
@@ -79,6 +81,8 @@
             int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime);
 
+    bool operator==(const NotifyKeyArgs& rhs) const;
+
     NotifyKeyArgs(const NotifyKeyArgs& other);
 
     virtual ~NotifyKeyArgs() { }
@@ -134,6 +138,8 @@
 
     virtual ~NotifyMotionArgs() { }
 
+    bool operator==(const NotifyMotionArgs& rhs) const;
+
     virtual void notify(const sp<InputListenerInterface>& listener) const;
 };
 
@@ -152,6 +158,8 @@
 
     NotifySwitchArgs(const NotifySwitchArgs& other);
 
+    bool operator==(const NotifySwitchArgs rhs) const;
+
     virtual ~NotifySwitchArgs() { }
 
     virtual void notify(const sp<InputListenerInterface>& listener) const;
@@ -170,6 +178,8 @@
 
     NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
 
+    bool operator==(const NotifyDeviceResetArgs& rhs) const;
+
     virtual ~NotifyDeviceResetArgs() { }
 
     virtual void notify(const sp<InputListenerInterface>& listener) const;
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/Android.bp b/services/inputflinger/tests/Android.bp
index 5b275fb..1835449 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -3,8 +3,11 @@
 cc_test {
     name: "inputflinger_tests",
     srcs: [
-        "InputReader_test.cpp",
+        "BlockingQueue_test.cpp",
+        "TestInputListener.cpp",
+        "InputClassifier_test.cpp",
         "InputDispatcher_test.cpp",
+        "InputReader_test.cpp",
     ],
     cflags: [
         "-Wall",
@@ -13,6 +16,7 @@
         "-Wno-unused-parameter",
     ],
     shared_libs: [
+        "android.hardware.input.classifier@1.0",
         "libbase",
         "libbinder",
         "libcutils",
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
new file mode 100644
index 0000000..0dea8d7
--- /dev/null
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../BlockingQueue.h"
+
+
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+
+// --- BlockingQueueTest ---
+
+/**
+ * Sanity check of basic pop and push operation.
+ */
+TEST(BlockingQueueTest, Queue_AddAndRemove) {
+    constexpr size_t capacity = 10;
+    BlockingQueue<int> queue(capacity);
+
+    ASSERT_TRUE(queue.push(1));
+    ASSERT_EQ(queue.pop(), 1);
+}
+
+/**
+ * Make sure the queue has strict capacity limits.
+ */
+TEST(BlockingQueueTest, Queue_ReachesCapacity) {
+    constexpr size_t capacity = 3;
+    BlockingQueue<int> queue(capacity);
+
+    // First 3 elements should be added successfully
+    ASSERT_TRUE(queue.push(1));
+    ASSERT_TRUE(queue.push(2));
+    ASSERT_TRUE(queue.push(3));
+    ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
+}
+
+/**
+ * Make sure the queue maintains FIFO order.
+ * Add elements and remove them, and check the order.
+ */
+TEST(BlockingQueueTest, Queue_isFIFO) {
+    constexpr size_t capacity = 10;
+    BlockingQueue<int> queue(capacity);
+
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_TRUE(queue.push(static_cast<int>(i)));
+    }
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_EQ(queue.pop(), static_cast<int>(i));
+    }
+}
+
+TEST(BlockingQueueTest, Queue_Clears) {
+    constexpr size_t capacity = 2;
+    BlockingQueue<int> queue(capacity);
+
+    queue.push(1);
+    queue.push(2);
+    queue.clear();
+    queue.push(3);
+    // Should no longer receive elements 1 and 2
+    ASSERT_EQ(3, queue.pop());
+}
+
+TEST(BlockingQueueTest, Queue_Erases) {
+    constexpr size_t capacity = 4;
+    BlockingQueue<int> queue(capacity);
+
+    queue.push(1);
+    queue.push(2);
+    queue.push(3);
+    queue.push(4);
+    // Erase elements 2 and 4
+    queue.erase([](int element) { return element == 2 || element == 4; });
+    // Should no longer receive elements 2 and 4
+    ASSERT_EQ(1, queue.pop());
+    ASSERT_EQ(3, queue.pop());
+}
+
+// --- BlockingQueueTest - Multiple threads ---
+
+TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) {
+    constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap
+    BlockingQueue<int> queue(capacity);
+
+    // Fill queue from a different thread
+    std::thread fillQueue([&queue](){
+        for (size_t i = 0; i < capacity; i++) {
+            ASSERT_TRUE(queue.push(static_cast<int>(i)));
+        }
+    });
+
+    // Make sure all elements are received in correct order
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_EQ(queue.pop(), static_cast<int>(i));
+    }
+
+    fillQueue.join();
+}
+
+/**
+ * When the queue has no elements, and pop is called, it should block
+ * the current thread until an element is added to the queue (from another thread).
+ * Here we create a separate thread and call pop on an empty queue. Next,
+ * we check that the thread is blocked.
+ */
+TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) {
+    constexpr size_t capacity = 1;
+    BlockingQueue<int> queue(capacity);
+
+    std::atomic_bool hasReceivedElement = false;
+
+    // fill queue from a different thread
+    std::thread waitUntilHasElements([&queue, &hasReceivedElement](){
+        queue.pop(); // This should block until an element has been added
+        hasReceivedElement = true;
+    });
+
+    ASSERT_FALSE(hasReceivedElement);
+    queue.push(1);
+    waitUntilHasElements.join();
+    ASSERT_TRUE(hasReceivedElement);
+}
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
new file mode 100644
index 0000000..20699c9
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../InputClassifier.h"
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using namespace android::hardware::input;
+
+namespace android {
+
+// --- InputClassifierTest ---
+
+static NotifyMotionArgs generateBasicMotionArgs() {
+    // Create a basic motion event for testing
+    constexpr size_t pointerCount = 1;
+    PointerProperties properties[pointerCount];
+    properties[0].id = 0;
+    properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    PointerCoords coords[pointerCount];
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+    static constexpr nsecs_t downTime = 2;
+    NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
+            AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
+            0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
+            AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
+            0/*pointerCount*/, properties, coords, 0/*xPrecision*/, 0/*yPrecision*/,
+            downTime, {}/*videoFrames*/);
+    return motionArgs;
+}
+
+class InputClassifierTest : public testing::Test {
+protected:
+    sp<InputClassifierInterface> mClassifier;
+    sp<TestInputListener> mTestListener;
+
+    virtual void SetUp() override {
+        mTestListener = new TestInputListener();
+        mClassifier = new InputClassifier(mTestListener);
+    }
+
+    virtual void TearDown() override {
+        mClassifier.clear();
+        mTestListener.clear();
+    }
+};
+
+/**
+ * Create a basic configuration change and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) {
+    // Create a basic configuration change and send to classifier
+    NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/);
+
+    mClassifier->notifyConfigurationChanged(&args);
+    NotifyConfigurationChangedArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
+    // Create a basic key event and send to classifier
+    NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
+            ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
+            AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+
+    mClassifier->notifyKey(&args);
+    NotifyKeyArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+
+/**
+ * Create a basic motion event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+    mClassifier->notifyMotion(&motionArgs);
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(motionArgs, args);
+}
+
+/**
+ * Create a basic switch event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) {
+    NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/,
+            5/*switchMask*/);
+
+    mClassifier->notifySwitch(&args);
+    NotifySwitchArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+/**
+ * Create a basic device reset event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
+    NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+
+    mClassifier->notifyDeviceReset(&args);
+    NotifyDeviceResetArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+// --- MotionClassifierTest ---
+
+class MotionClassifierTest : public testing::Test {
+protected:
+    std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
+
+    virtual void SetUp() override {
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+                classifier::V1_0::IInputClassifier::getService();
+        if (service) {
+            mMotionClassifier = std::make_unique<MotionClassifier>(service);
+        }
+    }
+};
+
+/**
+ * Since MotionClassifier creates a new thread to communicate with HAL,
+ * it's not really expected to ever exit. However, for testing purposes,
+ * we need to ensure that it is able to exit cleanly.
+ * If the thread is not properly cleaned up, it will generate SIGABRT.
+ * The logic for exiting the thread and cleaning up the resources is inside
+ * the destructor. Here, we just make sure the destructor does not crash.
+ */
+TEST_F(MotionClassifierTest, Destructor_DoesNotCrash) {
+    mMotionClassifier = nullptr;
+}
+
+/**
+ * Make sure MotionClassifier can handle events that don't have any
+ * video frames.
+ */
+TEST_F(MotionClassifierTest, Classify_NoVideoFrames) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure nothing crashes when a videoFrame is sent.
+ */
+TEST_F(MotionClassifierTest, Classify_OneVideoFrame) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    std::vector<int16_t> videoData = {1, 2, 3, 4};
+    timeval timestamp = { 1, 1};
+    TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+    motionArgs.videoFrames = {frame};
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure nothing crashes when 2 videoFrames are sent.
+ */
+TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    std::vector<int16_t> videoData1 = {1, 2, 3, 4};
+    timeval timestamp1 = { 1, 1};
+    TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1);
+
+    std::vector<int16_t> videoData2 = {6, 6, 6, 6};
+    timeval timestamp2 = { 1, 2};
+    TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2);
+
+    motionArgs.videoFrames = {frame1, frame2};
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when it is reset.
+ */
+TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset());
+    }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when a device is reset.
+ */
+TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) {
+    NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args));
+    }
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ed177fb..0f987c3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -444,7 +444,7 @@
     }
 
     virtual bool updateInfo() {
-        mInfo.token = mServerChannel->getToken();
+        mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
         mInfo.name = mName;
         mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
@@ -472,6 +472,11 @@
     void setFocus() {
         mFocused = true;
     }
+
+    void releaseChannel() {
+        mServerChannel.clear();
+        InputWindowHandle::releaseChannel();
+    }
 protected:
     virtual bool handled() {
         return true;
@@ -659,24 +664,25 @@
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
-    windowTop->setFocus();
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
+    windowTop->setFocus();
+    windowSecond->setFocus();
     Vector<sp<InputWindowHandle>> inputWindowHandles;
     inputWindowHandles.add(windowTop);
     inputWindowHandles.add(windowSecond);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-
     // Release channel for window is no longer valid.
     windowTop->releaseChannel();
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
 
-    // Test inject a motion down, should timeout because of no target channel.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+    // Test inject a key down, should dispatch to a valid window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top window is invalid, so it should not receive any input event.
     windowTop->assertNoEvents();
-    windowSecond->assertNoEvents();
+    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 }
 
 /* Test InputDispatcher for MultiDisplay */
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index de87e7f..84c5ad6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -15,12 +15,13 @@
  */
 
 #include "../InputReader.h"
+#include "TestInputListener.h"
 
-#include <inttypes.h>
-#include <utils/List.h>
 #include <gtest/gtest.h>
+#include <inttypes.h>
 #include <math.h>
 
+
 namespace android {
 
 // An arbitrary time value.
@@ -273,114 +274,6 @@
     }
 };
 
-
-// --- FakeInputListener ---
-
-class FakeInputListener : public InputListenerInterface {
-private:
-    List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
-    List<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
-    List<NotifyKeyArgs> mNotifyKeyArgsQueue;
-    List<NotifyMotionArgs> mNotifyMotionArgsQueue;
-    List<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
-protected:
-    virtual ~FakeInputListener() { }
-
-public:
-    FakeInputListener() {
-    }
-
-    void assertNotifyConfigurationChangedWasCalled(
-            NotifyConfigurationChangedArgs* outEventArgs = nullptr) {
-        ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
-                << "Expected notifyConfigurationChanged() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
-        }
-        mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
-    }
-
-    void assertNotifyConfigurationChangedWasNotCalled() {
-        ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
-                << "Expected notifyConfigurationChanged() to not have been called.";
-    }
-
-    void assertNotifyDeviceResetWasCalled(
-            NotifyDeviceResetArgs* outEventArgs = nullptr) {
-        ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
-                << "Expected notifyDeviceReset() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
-        }
-        mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
-    }
-
-    void assertNotifyDeviceResetWasNotCalled() {
-        ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
-                << "Expected notifyDeviceReset() to not have been called.";
-    }
-
-    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr) {
-        ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
-                << "Expected notifyKey() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyKeyArgsQueue.begin();
-        }
-        mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
-    }
-
-    void assertNotifyKeyWasNotCalled() {
-        ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
-                << "Expected notifyKey() to not have been called.";
-    }
-
-    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr) {
-        ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
-                << "Expected notifyMotion() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyMotionArgsQueue.begin();
-        }
-        mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
-    }
-
-    void assertNotifyMotionWasNotCalled() {
-        ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
-                << "Expected notifyMotion() to not have been called.";
-    }
-
-    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr) {
-        ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
-                << "Expected notifySwitch() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifySwitchArgsQueue.begin();
-        }
-        mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
-    }
-
-private:
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-        mNotifyConfigurationChangedArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-        mNotifyDeviceResetArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyKey(const NotifyKeyArgs* args) {
-        mNotifyKeyArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyMotion(const NotifyMotionArgs* args) {
-        mNotifyMotionArgsQueue.push_back(*args);
-    }
-
-    virtual void notifySwitch(const NotifySwitchArgs* args) {
-        mNotifySwitchArgsQueue.push_back(*args);
-    }
-};
-
-
 // --- FakeEventHub ---
 
 class FakeEventHub : public EventHubInterface {
@@ -948,6 +841,7 @@
     bool mResetWasCalled;
     bool mProcessWasCalled;
 
+    std::optional<DisplayViewport> mViewport;
 public:
     FakeInputMapper(InputDevice* device, uint32_t sources) :
             InputMapper(device),
@@ -1016,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) {
@@ -1064,6 +964,13 @@
 
     virtual void fadePointer() {
     }
+
+    virtual std::optional<int32_t> getAssociatedDisplay() {
+        if (mViewport) {
+            return std::make_optional(mViewport->displayId);
+        }
+        return std::nullopt;
+    }
 };
 
 
@@ -1091,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);
@@ -1303,7 +1211,7 @@
 
 class InputReaderTest : public testing::Test {
 protected:
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<FakeEventHub> mFakeEventHub;
     sp<InstrumentedInputReader> mReader;
@@ -1311,7 +1219,7 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
 
         mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
     }
@@ -1393,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);
@@ -1577,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);
@@ -1607,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 ---
 
@@ -1620,7 +1558,7 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
     InputDevice* mDevice;
@@ -1628,7 +1566,7 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
         mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
@@ -1815,14 +1753,14 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
new file mode 100644
index 0000000..3ee33f1
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+TestInputListener::TestInputListener() { }
+
+TestInputListener::~TestInputListener() { }
+
+void TestInputListener::assertNotifyConfigurationChangedWasCalled(
+        NotifyConfigurationChangedArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
+            << "Expected notifyConfigurationChanged() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
+    }
+    mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
+    ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
+            << "Expected notifyConfigurationChanged() to not have been called.";
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+        NotifyDeviceResetArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
+            << "Expected notifyDeviceReset() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
+    }
+    mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
+    ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
+            << "Expected notifyDeviceReset() to not have been called.";
+}
+
+void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyKeyArgsQueue.begin();
+    }
+    mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyKeyWasNotCalled() {
+    ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+}
+
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyMotionArgsQueue.begin();
+    }
+    mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyMotionWasNotCalled() {
+    ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
+            << "Expected notifyMotion() to not have been called.";
+}
+
+void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
+            << "Expected notifySwitch() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifySwitchArgsQueue.begin();
+    }
+    mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+}
+
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+    mNotifyConfigurationChangedArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+    mNotifyDeviceResetArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+    mNotifyKeyArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+    mNotifyMotionArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+        mNotifySwitchArgsQueue.push_back(*args);
+    }
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
new file mode 100644
index 0000000..085d343
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_TEST_INPUT_LISTENER_H
+#define _UI_TEST_INPUT_LISTENER_H
+
+#include <gtest/gtest.h>
+#include "InputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+class TestInputListener : public InputListenerInterface {
+private:
+    std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
+    std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
+    std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
+    std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
+    std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
+
+protected:
+    virtual ~TestInputListener();
+
+public:
+    TestInputListener();
+
+    void assertNotifyConfigurationChangedWasCalled(
+            NotifyConfigurationChangedArgs* outEventArgs = nullptr);
+
+    void assertNotifyConfigurationChangedWasNotCalled();
+
+    void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
+
+    void assertNotifyDeviceResetWasNotCalled();
+
+    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+
+    void assertNotifyKeyWasNotCalled();
+
+    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+
+    void assertNotifyMotionWasNotCalled();
+
+    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+
+private:
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+    virtual void notifyKey(const NotifyKeyArgs* args);
+
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+
+    virtual void notifySwitch(const NotifySwitchArgs* args);
+};
+
+} // namespace android
+#endif
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/Android.bp b/services/surfaceflinger/Android.bp
index 2a70293..f514fe3 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -152,6 +152,7 @@
         "Scheduler/MessageQueue.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
+        "Scheduler/PhaseOffsets.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceInterceptor.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4eafeac..9662fcd 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -55,8 +55,6 @@
       : Layer(args), mTextureName(args.flinger->getNewTexture()) {
     ALOGV("Creating Layer %s", args.name.string());
 
-    mTexture.init(renderengine::Texture::TEXTURE_EXTERNAL, mTextureName);
-
     mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
 
     mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -127,13 +125,11 @@
     return inverse(tr);
 }
 
-/*
- * onDraw will draw the current layer onto the presentable buffer
- */
-void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip,
-                         bool useIdentityTransform) {
+bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                     bool useIdentityTransform, Region& clearRegion,
+                                     renderengine::LayerSettings& layer) {
     ATRACE_CALL();
-
+    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
     if (CC_UNLIKELY(mActiveBuffer == 0)) {
         // the texture has not been created yet, this Layer has
         // in fact never been drawn into. This happens frequently with
@@ -151,30 +147,27 @@
                 finished = true;
                 return;
             }
-            under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
+            under.orSelf(layer->visibleRegion);
         });
         // if not everything below us is covered, we plug the holes!
         Region holes(clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearWithOpenGL(renderArea, 0, 0, 0, 1);
+            clearRegion.orSelf(holes);
         }
-        return;
+        return false;
     }
-
-    // Bind the current buffer to the GL texture, and wait for it to be
-    // ready for us to draw into.
-    status_t err = bindTextureImage();
-    if (err != NO_ERROR) {
-        ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
-        // Go ahead and draw the buffer anyway; no matter what we do the screen
-        // is probably going to have something visibly wrong.
-    }
-
     bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
-
-    auto& engine(mFlinger->getRenderEngine());
-
+    const State& s(getDrawingState());
     if (!blackOutLayer) {
+        layer.source.buffer.buffer = mActiveBuffer;
+        layer.source.buffer.isOpaque = isOpaque(s);
+        layer.source.buffer.fence = mActiveBufferFence;
+        layer.source.buffer.cacheHint = useCachedBufferForClientComposition()
+                ? renderengine::Buffer::CachingHint::USE_CACHE
+                : renderengine::Buffer::CachingHint::NO_CACHE;
+        layer.source.buffer.textureName = mTextureName;
+        layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+        layer.source.buffer.isY410BT2020 = isHdrY410();
         // TODO: we could be more subtle with isFixedSize()
         const bool useFiltering = needsFiltering() || renderArea.needsFiltering() || isFixedSize();
 
@@ -211,17 +204,31 @@
             memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
         }
 
-        // Set things up for texturing.
-        mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
-        mTexture.setFiltering(useFiltering);
-        mTexture.setMatrix(textureMatrix);
+        const Rect win{computeBounds()};
+        const float bufferWidth = getBufferSize(s).getWidth();
+        const float bufferHeight = getBufferSize(s).getHeight();
 
-        engine.setupLayerTexturing(mTexture);
+        const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+        const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+        const float translateY = float(win.top) / bufferHeight;
+        const float translateX = float(win.left) / bufferWidth;
+
+        // Flip y-coordinates because GLConsumer expects OpenGL convention.
+        mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+                mat4::translate(vec4(-.5, -.5, 0, 1)) *
+                mat4::translate(vec4(translateX, translateY, 0, 1)) *
+                mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
+
+        layer.source.buffer.useTextureFiltering = useFiltering;
+        layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
     } else {
-        engine.setupLayerBlackedOut();
+        // If layer is blacked out, force alpha to 1 so that we draw a black color
+        // layer.
+        layer.source.buffer.buffer = nullptr;
+        layer.alpha = 1.0;
     }
-    drawWithOpenGL(renderArea, useIdentityTransform);
-    engine.disableTexturing();
+
+    return true;
 }
 
 bool BufferLayer::isHdrY410() const {
@@ -604,67 +611,6 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
-    ATRACE_CALL();
-    const State& s(getDrawingState());
-
-    computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
-
-    /*
-     * NOTE: the way we compute the texture coordinates here produces
-     * different results than when we take the HWC path -- in the later case
-     * the "source crop" is rounded to texel boundaries.
-     * This can produce significantly different results when the texture
-     * is scaled by a large amount.
-     *
-     * The GL code below is more logical (imho), and the difference with
-     * HWC is due to a limitation of the HWC API to integers -- a question
-     * is suspend is whether we should ignore this problem or revert to
-     * GL composition when a buffer scaling is applied (maybe with some
-     * minimal value)? Or, we could make GL behave like HWC -- but this feel
-     * like more of a hack.
-     */
-    const Rect bounds{computeBounds()}; // Rounds from FloatRect
-
-    Rect win = bounds;
-    const int bufferWidth = getBufferSize(s).getWidth();
-    const int bufferHeight = getBufferSize(s).getHeight();
-
-    const float left = float(win.left) / float(bufferWidth);
-    const float top = float(win.top) / float(bufferHeight);
-    const float right = float(win.right) / float(bufferWidth);
-    const float bottom = float(win.bottom) / float(bufferHeight);
-
-    // TODO: we probably want to generate the texture coords with the mesh
-    // here we assume that we only have 4 vertices
-    renderengine::Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>());
-    // flip texcoords vertically because BufferLayerConsumer expects them to be in GL convention
-    texCoords[0] = vec2(left, 1.0f - top);
-    texCoords[1] = vec2(left, 1.0f - bottom);
-    texCoords[2] = vec2(right, 1.0f - bottom);
-    texCoords[3] = vec2(right, 1.0f - top);
-
-    const auto roundedCornerState = getRoundedCornerState();
-    const auto cropRect = roundedCornerState.cropRect;
-    setupRoundedCornersCropCoordinates(win, cropRect);
-
-    auto& engine(mFlinger->getRenderEngine());
-    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
-                              getColor(), roundedCornerState.radius);
-    engine.setSourceDataSpace(mCurrentDataSpace);
-
-    if (isHdrY410()) {
-        engine.setSourceY410BT2020(true);
-    }
-
-    engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
-
-    engine.drawMesh(getBE().mMesh);
-    engine.disableBlending();
-
-    engine.setSourceY410BT2020(false);
-}
-
 uint64_t BufferLayer::getHeadFrameNumber() const {
     if (hasFrameUpdate()) {
         return getFrameNumber();
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index e3b7f2f..3fe8ed0 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -28,6 +28,7 @@
 
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
+#include <renderengine/Image.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/Texture.h>
 #include <ui/FrameStats.h>
@@ -78,10 +79,6 @@
     // isFixedSize - true if content has a fixed size
     bool isFixedSize() const override;
 
-    // onDraw - draws the surface.
-    void onDraw(const RenderArea& renderArea, const Region& clip,
-                bool useIdentityTransform) override;
-
     bool isHdrY410() const override;
 
     void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -169,13 +166,19 @@
 
     bool mRefreshPending{false};
 
+    // Returns true if, when drawing the active buffer during gpu compositon, we
+    // should use a cached buffer or not.
+    virtual bool useCachedBufferForClientComposition() const = 0;
+
+    // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                            bool useIdentityTransform, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
+
 private:
     // Returns true if this layer requires filtering
     bool needsFiltering() const;
 
-    // drawing
-    void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const;
-
     uint64_t getHeadFrameNumber() const;
 
     uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
@@ -183,9 +186,6 @@
     // main thread.
     bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
 
-    // The texture used to draw the layer in GLES composition mode
-    mutable renderengine::Texture mTexture;
-
     Rect getBufferSize(const State& s) const override;
 };
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6826050..7ed8184 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -272,6 +272,7 @@
     // Update the BufferLayerConsumer state.
     mCurrentTexture = slot;
     mCurrentTextureBuffer = nextTextureBuffer;
+    mCurrentTextureBufferStaleForGpu = false;
     mCurrentTextureImageFreed = nullptr;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
@@ -294,48 +295,7 @@
 status_t BufferLayerConsumer::bindTextureImageLocked() {
     ATRACE_CALL();
 
-    mRE.checkErrors();
-
-    // It is possible for the current slot's buffer to be freed before a new one
-    // is bound. In that scenario we still want to bind the image.
-    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) {
-        BLC_LOGE("bindTextureImage: no currently-bound texture");
-        mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
-        return NO_INIT;
-    }
-
-    renderengine::Image* imageToRender;
-
-    // mCurrentTextureImageFreed is non-null iff mCurrentTexture ==
-    // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check.
-    if (mCurrentTextureImageFreed) {
-        imageToRender = mCurrentTextureImageFreed.get();
-    } else if (mImages[mCurrentTexture]) {
-        imageToRender = mImages[mCurrentTexture].get();
-    } else {
-        std::unique_ptr<renderengine::Image> image = mRE.createImage();
-        bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(),
-                                                    mCurrentTextureBuffer->getUsage() &
-                                                            GRALLOC_USAGE_PROTECTED);
-        if (!success) {
-            BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64
-                     " fmt=%d",
-                     mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(),
-                     mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(),
-                     mCurrentTextureBuffer->getPixelFormat());
-            BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
-            mRE.bindExternalTextureImage(mTexName, *image);
-            return UNKNOWN_ERROR;
-        }
-        imageToRender = image.get();
-        // Cache the image here so that we can reuse it.
-        mImages[mCurrentTexture] = std::move(image);
-    }
-
-    mRE.bindExternalTextureImage(mTexName, *imageToRender);
-
-    // Wait for the new buffer to be ready.
-    return doFenceWaitLocked();
+    return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false);
 }
 
 status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) {
@@ -433,16 +393,29 @@
     return mCurrentApi;
 }
 
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
     Mutex::Autolock lock(mMutex);
 
     if (outSlot != nullptr) {
         *outSlot = mCurrentTexture;
     }
 
+    if (outFence != nullptr) {
+        *outFence = mCurrentFence;
+    }
+
     return mCurrentTextureBuffer;
 }
 
+bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() {
+    Mutex::Autolock lock(mMutex);
+    bool useCache = mCurrentTextureBufferStaleForGpu;
+    // Set the staleness bit here, as this function is only called during a
+    // client composition path.
+    mCurrentTextureBufferStaleForGpu = true;
+    return useCache;
+}
+
 Rect BufferLayerConsumer::getCurrentCrop() const {
     Mutex::Autolock lock(mMutex);
     return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index ea46245..e2ef399 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -150,10 +150,16 @@
     // for use with bilinear filtering.
     void setFilteringEnabled(bool enabled);
 
+    // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the
+    // buffer is now "stale" for GPU composition, and returns the old staleness
+    // bit as a caching hint.
+    bool getAndSetCurrentBufferCacheHint();
+
     // getCurrentBuffer returns the buffer associated with the current image.
     // When outSlot is not nullptr, the current buffer slot index is also
-    // returned.
-    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
+    // returned. Simiarly, when outFence is not nullptr, the current output
+    // fence is returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
 
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
@@ -255,6 +261,10 @@
     // must track it separately in order to support the getCurrentBuffer method.
     sp<GraphicBuffer> mCurrentTextureBuffer;
 
+    // True if the buffer was used for the previous client composition frame,
+    // and false otherwise.
+    bool mCurrentTextureBufferStaleForGpu;
+
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
     Rect mCurrentCrop;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 42021d1..edfeb0b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -306,7 +306,7 @@
 
 status_t BufferQueueLayer::updateActiveBuffer() {
     // update the active buffer
-    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot);
+    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
     getBE().compositionInfo.mBuffer = mActiveBuffer;
     getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
 
@@ -317,6 +317,10 @@
     return NO_ERROR;
 }
 
+bool BufferQueueLayer::useCachedBufferForClientComposition() const {
+    return mConsumer->getAndSetCurrentBufferCacheHint();
+}
+
 status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
     mPreviousFrameNumber = mCurrentFrameNumber;
     mCurrentFrameNumber = mConsumer->getFrameNumber();
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index d7c3f6a..20992a4 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -62,6 +62,9 @@
 public:
     bool fenceHasSignaled() const override;
 
+protected:
+    bool useCachedBufferForClientComposition() const override;
+
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2132f59..96a66cb 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) {
@@ -449,46 +492,7 @@
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
 
-    engine.checkErrors();
-
-    // TODO(marissaw): once buffers are cached, don't create a new image everytime
-    mTextureImage = engine.createImage();
-
-    bool created =
-            mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(),
-                                                 s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-    if (!created) {
-        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
-              s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(),
-              s.buffer->getUsage(), s.buffer->getPixelFormat());
-        engine.bindExternalTextureImage(mTextureName, *engine.createImage());
-        return NO_INIT;
-    }
-
-    engine.bindExternalTextureImage(mTextureName, *mTextureImage);
-
-    // Wait for the new buffer to be ready.
-    if (s.acquireFence->isValid()) {
-        if (SyncFeatures::getInstance().useWaitSync()) {
-            base::unique_fd fenceFd(s.acquireFence->dup());
-            if (fenceFd == -1) {
-                ALOGE("error dup'ing fence fd: %d", errno);
-                return -errno;
-            }
-            if (!engine.waitFence(std::move(fenceFd))) {
-                ALOGE("failed to wait on fence fd");
-                return UNKNOWN_ERROR;
-            }
-        } else {
-            status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage");
-            if (err != NO_ERROR) {
-                ALOGE("error waiting for fence: %d", err);
-                return err;
-            }
-        }
-    }
-
-    return NO_ERROR;
+    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false);
 }
 
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
@@ -611,12 +615,18 @@
     }
 
     mActiveBuffer = s.buffer;
+    mActiveBufferFence = s.acquireFence;
     getBE().compositionInfo.mBuffer = mActiveBuffer;
     getBE().compositionInfo.mBufferSlot = 0;
 
     return NO_ERROR;
 }
 
+bool BufferStateLayer::useCachedBufferForClientComposition() const {
+    // TODO: Store a proper staleness bit to support EGLImage caching.
+    return false;
+}
+
 status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
     // TODO(marissaw): support frame history events
     mCurrentFrameNumber = mFrameNumber;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 3f891d3..bef9f19 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*/,
@@ -95,6 +98,9 @@
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
 
+protected:
+    bool useCachedBufferForClientComposition() const override;
+
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0e447d8..0ca3759 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -73,14 +73,10 @@
     return lbc;
 }
 
-
-status_t Client::createSurface(
-        const String8& name,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
-        sp<IBinder>* handle,
-        sp<IGraphicBufferProducer>* gbp)
-{
+status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+                               uint32_t flags, const sp<IBinder>& parentHandle,
+                               LayerMetadata metadata, sp<IBinder>* handle,
+                               sp<IGraphicBufferProducer>* gbp) {
     sp<Layer> parent = nullptr;
     if (parentHandle != nullptr) {
         auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
@@ -91,14 +87,14 @@
     }
 
     // We rely on createLayer to check permissions.
-    return mFlinger->createLayer(name, this, w, h, format, flags, windowType,
-                                 ownerUid, handle, gbp, &parent);
+    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+                                 &parent);
 }
 
 status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                          PixelFormat format, uint32_t flags,
                                          const sp<IGraphicBufferProducer>& parent,
-                                         int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
+                                         LayerMetadata metadata, sp<IBinder>* handle,
                                          sp<IGraphicBufferProducer>* gbp) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         return BAD_VALUE;
@@ -111,8 +107,7 @@
 
     sp<IBinder> parentHandle = layer->getHandle();
 
-    return createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, handle,
-                         gbp);
+    return createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), handle, gbp);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 0f5ee4c..74e4818 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -51,18 +51,16 @@
 
 private:
     // ISurfaceComposerClient interface
-    virtual status_t createSurface(
-            const String8& name,
-            uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
-            const sp<IBinder>& parent, int32_t windowType, int32_t ownerUid,
-            sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp);
+    virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+                                   uint32_t flags, const sp<IBinder>& parent,
+                                   LayerMetadata metadata, sp<IBinder>* handle,
+                                   sp<IGraphicBufferProducer>* gbp);
 
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
-                                             int32_t windowType, int32_t ownerUid,
-                                             sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp);
+                                             LayerMetadata metadata, sp<IBinder>* handle,
+                                             sp<IGraphicBufferProducer>* gbp);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index f27f6aa..34082c3 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -38,28 +38,14 @@
 
 ColorLayer::~ColorLayer() = default;
 
-void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
-                        bool useIdentityTransform) {
-    half4 color = getColor();
-    if (color.a > 0) {
-        renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2);
-        computeGeometry(renderArea, mesh, useIdentityTransform);
-        auto& engine(mFlinger->getRenderEngine());
-
-        Rect win{computeBounds()};
-
-        const auto roundedCornerState = getRoundedCornerState();
-        const auto cropRect = roundedCornerState.cropRect;
-        setupRoundedCornersCropCoordinates(win, cropRect);
-
-        engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
-                                  true /* disableTexture */, color, roundedCornerState.radius);
-
-        engine.setSourceDataSpace(mCurrentDataSpace);
-        engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
-        engine.drawMesh(mesh);
-        engine.disableBlending();
-    }
+bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer) {
+    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
+    half4 color(getColor());
+    half3 solidColor(color.r, color.g, color.b);
+    layer.source.solidColor = solidColor;
+    return true;
 }
 
 bool ColorLayer::isVisible() const {
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index d1b1697..f43fc3c 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -29,8 +29,6 @@
     ~ColorLayer() override;
 
     virtual const char* getTypeId() const { return "ColorLayer"; }
-    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
-                        bool useIdentityTransform);
     bool isVisible() const override;
 
     void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -40,6 +38,9 @@
 
 protected:
     FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
+    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 69b5728..6be4ad8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -89,10 +89,9 @@
     // TODO(lpique): Make this protected once it is only internally called.
     virtual OutputCompositionState& editState() = 0;
 
-    // Gets the physical space dirty region. If repaintEverything is true, this
-    // will be the full display bounds. Internally the dirty region is stored in
-    // logical (aka layer stack) space.
-    virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
+    // Gets the dirty region in layer stack space.
+    // If repaintEverything is true, this will be the full display bounds.
+    virtual Region getDirtyRegion(bool repaintEverything) const = 0;
 
     // Tests whether a given layerStackId belongs in this output.
     // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index dd01b05..ddeb730 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -75,16 +75,13 @@
     // Allocates a buffer as scratch space for GPU composition
     virtual sp<GraphicBuffer> dequeueBuffer() = 0;
 
-    // Queues the drawn buffer for consumption by HWC
-    virtual void queueBuffer() = 0;
+    // Queues the drawn buffer for consumption by HWC. readyFence is the fence
+    // which will fire when the buffer is ready for consumption.
+    virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
 
     // Called after the HWC calls are made to present the display
     virtual void onPresentDisplayCompleted() = 0;
 
-    // Marks the current buffer has finished, so that it can be presented and
-    // swapped out
-    virtual void finishBuffer() = 0;
-
     // Called to set the viewport and projection state for rendering into this
     // surface
     virtual void setViewportAndProjection() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 180c40b..4be0706 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -57,7 +57,7 @@
     const OutputCompositionState& getState() const override;
     OutputCompositionState& editState() override;
 
-    Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
+    Region getDirtyRegion(bool repaintEverything) const override;
     bool belongsInOutput(uint32_t, bool) const override;
 
     // Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 0489310..2f0fceb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -53,9 +53,8 @@
     status_t beginFrame(bool mustRecompose) override;
     status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
     sp<GraphicBuffer> dequeueBuffer() override;
-    void queueBuffer() override;
+    void queueBuffer(base::unique_fd&& readyFence) override;
     void onPresentDisplayCompleted() override;
-    void finishBuffer() override;
     void setViewportAndProjection() override;
     void flip() override;
 
@@ -77,9 +76,6 @@
     const sp<ANativeWindow> mNativeWindow;
     // Current buffer being rendered into
     sp<GraphicBuffer> mGraphicBuffer;
-    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
-    // that drawing to the buffer is now complete.
-    base::unique_fd mBufferReady;
     const sp<DisplaySurface> mDisplaySurface;
     ui::Size mSize;
     std::uint32_t mPageFlipCount{0};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 445f0bb..e767ad3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -53,7 +53,7 @@
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
 
-    MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
+    MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
     MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index 2269e57..e9ff330 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,9 +39,8 @@
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
     MOCK_METHOD1(prepareFrame, status_t(std::vector<CompositionInfo>& compositionData));
     MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>());
-    MOCK_METHOD0(queueBuffer, void());
+    MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
     MOCK_METHOD0(onPresentDisplayCompleted, void());
-    MOCK_METHOD0(finishBuffer, void());
     MOCK_METHOD0(setViewportAndProjection, void());
     MOCK_METHOD0(flip, void());
     MOCK_CONST_METHOD1(dump, void(std::string& result));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7fb67bd..c4d563f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -164,13 +164,10 @@
     return mState;
 }
 
-Region Output::getPhysicalSpaceDirtyRegion(bool repaintEverything) const {
-    Region dirty;
-    if (repaintEverything) {
-        dirty.set(mState.bounds);
-    } else {
-        dirty = mState.transform.transform(mState.dirtyRegion);
-        dirty.andSelf(mState.bounds);
+Region Output::getDirtyRegion(bool repaintEverything) const {
+    Region dirty(mState.viewport);
+    if (!repaintEverything) {
+        dirty.andSelf(mState.dirtyRegion);
     }
     return dirty;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index d546fc8..3f841d2 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -156,7 +156,7 @@
     return mGraphicBuffer;
 }
 
-void RenderSurface::queueBuffer() {
+void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
     auto& hwc = mCompositionEngine.getHwComposer();
     const auto id = mDisplay.getId();
 
@@ -178,9 +178,9 @@
         if (mGraphicBuffer == nullptr) {
             ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
         } else {
-            status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
-                                                         mGraphicBuffer->getNativeBuffer(),
-                                                         dup(mBufferReady));
+            status_t result =
+                    mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                               mGraphicBuffer->getNativeBuffer(), dup(readyFence));
             if (result != NO_ERROR) {
                 ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
                       result);
@@ -190,12 +190,10 @@
                     LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
                 } else {
                     mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(),
-                                                dup(mBufferReady));
+                                                mGraphicBuffer->getNativeBuffer(), dup(readyFence));
                 }
             }
 
-            mBufferReady.reset();
             mGraphicBuffer = nullptr;
         }
     }
@@ -217,14 +215,6 @@
                                           ui::Transform::ROT_0);
 }
 
-void RenderSurface::finishBuffer() {
-    auto& renderEngine = mCompositionEngine.getRenderEngine();
-    mBufferReady = renderEngine.flush();
-    if (mBufferReady.get() < 0) {
-        renderEngine.finish();
-    }
-}
-
 void RenderSurface::flip() {
     mPageFlipCount++;
 }
@@ -263,9 +253,5 @@
     return mGraphicBuffer;
 }
 
-base::unique_fd& RenderSurface::mutableBufferReadyForTest() {
-    return mBufferReady;
-}
-
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fe12825..0df1dab 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -212,52 +212,32 @@
 }
 
 /* ------------------------------------------------------------------------
- * Output::getPhysicalSpaceDirtyRegion()
+ * Output::getDirtyRegion()
  */
 
-TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
-    const Rect displaySize{100, 200};
-    mOutput.editState().bounds = displaySize;
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+    const Rect viewport{100, 200};
+    mOutput.editState().viewport = viewport;
     mOutput.editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+        Region result = mOutput.getDirtyRegion(true);
 
-        EXPECT_THAT(result, RegionEq(Region(displaySize)));
-    }
-
-    // For repaint everything == true, the returned value does not depend on the display
-    // rotation.
-    mOutput.editState().transform.set(ui::Transform::ROT_90, 0, 0);
-
-    {
-        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
-
-        EXPECT_THAT(result, RegionEq(Region(displaySize)));
+        EXPECT_THAT(result, RegionEq(Region(viewport)));
     }
 }
 
-TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
-    const Rect displaySize{100, 200};
-    mOutput.editState().bounds = displaySize;
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
+    const Rect viewport{100, 200};
+    mOutput.editState().viewport = viewport;
     mOutput.editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+        Region result = mOutput.getDirtyRegion(false);
 
         // The dirtyRegion should be clipped to the display bounds.
         EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
     }
-
-    mOutput.editState().transform.set(ui::Transform::ROT_90, displaySize.getWidth(),
-                                      displaySize.getHeight());
-
-    {
-        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
-
-        // The dirtyRegion should be rotated and clipped to the display bounds.
-        EXPECT_THAT(result, RegionEq(Region(Rect(100, 50))));
-    }
 }
 
 /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 13cc663..c56d92a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -373,7 +373,7 @@
             .WillOnce(Return(false));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer();
+    mSurface.queueBuffer(base::unique_fd());
 
     EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
 }
@@ -387,7 +387,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer();
+    mSurface.queueBuffer(base::unique_fd());
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -402,7 +402,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer();
+    mSurface.queueBuffer(base::unique_fd());
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -419,7 +419,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer();
+    mSurface.queueBuffer(base::unique_fd());
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -436,7 +436,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer();
+    mSurface.queueBuffer(base::unique_fd());
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -452,29 +452,6 @@
 }
 
 /* ------------------------------------------------------------------------
- * RenderSurface::finishBuffer()
- */
-
-TEST_F(RenderSurfaceTest, finishBufferJustFlushesRenderEngine) {
-    int fd = dup(1);
-
-    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(fd))));
-
-    mSurface.finishBuffer();
-
-    EXPECT_EQ(fd, mSurface.mutableBufferReadyForTest().release());
-}
-
-TEST_F(RenderSurfaceTest, finishBufferFlushesAndFinishesRenderEngine) {
-    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(-2))));
-    EXPECT_CALL(mRenderEngine, finish()).Times(1);
-
-    mSurface.finishBuffer();
-
-    EXPECT_EQ(-2, mSurface.mutableBufferReadyForTest().release());
-}
-
-/* ------------------------------------------------------------------------
  * RenderSurface::setViewportAndProjection()
  */
 
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index ca49f6c..9530398 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -26,7 +26,10 @@
 
 ContainerLayer::~ContainerLayer() = default;
 
-void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) {}
+bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&,
+                                        renderengine::LayerSettings&) {
+    return false;
+}
 
 bool ContainerLayer::isVisible() const {
     return !isHiddenByPolicy();
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 413844b..409cf22 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -29,8 +29,6 @@
     ~ContainerLayer() override;
 
     const char* getTypeId() const override { return "ContainerLayer"; }
-    void onDraw(const RenderArea& renderArea, const Region& clip,
-                bool useIdentityTransform) override;
     bool isVisible() const override;
 
     void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -39,6 +37,11 @@
     bool isCreatedFromMainThread() const override { return true; }
 
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                            bool useIdentityTransform, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 03eafd5..8343e5a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -260,17 +260,20 @@
     const uint32_t bufferSlotCount = 1;
     Error error = kDefaultError;
     if (mClient_2_2) {
-        mClient_2_2->createVirtualDisplay_2_2(width, height, *format, bufferSlotCount,
-                [&](const auto& tmpError, const auto& tmpDisplay,
-                    const auto& tmpFormat) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
+        mClient_2_2->createVirtualDisplay_2_2(width, height,
+                                              static_cast<types::V1_1::PixelFormat>(*format),
+                                              bufferSlotCount,
+                                              [&](const auto& tmpError, const auto& tmpDisplay,
+                                                  const auto& tmpFormat) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
 
-                    *outDisplay = tmpDisplay;
-                    *format = tmpFormat;
-                });
+                                                  *outDisplay = tmpDisplay;
+                                                  *format = static_cast<types::V1_2::PixelFormat>(
+                                                          tmpFormat);
+                                              });
     } else {
         mClient->createVirtualDisplay(width, height,
                 static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index ba3d2a6..542e840 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -48,11 +48,11 @@
 
 using types::V1_0::ColorTransform;
 using types::V1_0::Transform;
-using types::V1_1::PixelFormat;
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
 using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
 
 using V2_1::Config;
 using V2_1::Display;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5308302..619964f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -94,8 +94,6 @@
     mCurrentState.layerStack = 0;
     mCurrentState.sequence = 0;
     mCurrentState.requested_legacy = mCurrentState.active_legacy;
-    mCurrentState.appId = 0;
-    mCurrentState.type = 0;
     mCurrentState.active.w = UINT32_MAX;
     mCurrentState.active.h = UINT32_MAX;
     mCurrentState.active.transform.set(0, 0);
@@ -595,14 +593,16 @@
              to_string(error).c_str(), static_cast<int32_t>(error));
     getBE().compositionInfo.hwc.z = z;
 
-    int type = s.type;
-    int appId = s.appId;
+    int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+    int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0);
     sp<Layer> parent = mDrawingParent.promote();
     if (parent.get()) {
         auto& parentState = parent->getDrawingState();
-        if (parentState.type >= 0 || parentState.appId >= 0) {
-            type = parentState.type;
-            appId = parentState.appId;
+        const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+        const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
+        if (parentType >= 0 || parentAppId >= 0) {
+            type = parentType;
+            appId = parentAppId;
         }
     }
 
@@ -639,7 +639,7 @@
          * Here we cancel out the orientation component of the WM transform.
          * The scaling and translate components are already included in our bounds
          * computation so it's enough to just omit it in the composition.
-         * See comment in onDraw with ref to b/36727915 for why.
+         * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
          */
         transform = ui::Transform(invTransform) * tr * bufferOrientation;
     }
@@ -707,12 +707,51 @@
 // drawing...
 // ---------------------------------------------------------------------------
 
-void Layer::draw(const RenderArea& renderArea, const Region& clip) {
-    onDraw(renderArea, clip, false);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                               Region& clearRegion, renderengine::LayerSettings& layer) {
+    return prepareClientLayer(renderArea, clip, false, clearRegion, layer);
 }
 
-void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) {
-    onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+                               Region& clearRegion, renderengine::LayerSettings& layer) {
+    return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
+                              clearRegion, layer);
+}
+
+bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
+                               bool useIdentityTransform, Region& /*clearRegion*/,
+                               renderengine::LayerSettings& layer) {
+    FloatRect bounds = computeBounds();
+    half alpha = getAlpha();
+    layer.geometry.boundaries = bounds;
+    if (useIdentityTransform) {
+        layer.geometry.positionTransform = mat4();
+    } else {
+        const ui::Transform transform = getTransform();
+        mat4 m;
+        m[0][0] = transform[0][0];
+        m[0][1] = transform[0][1];
+        m[0][3] = transform[0][2];
+        m[1][0] = transform[1][0];
+        m[1][1] = transform[1][1];
+        m[1][3] = transform[1][2];
+        m[3][0] = transform[2][0];
+        m[3][1] = transform[2][1];
+        m[3][3] = transform[2][2];
+        layer.geometry.positionTransform = m;
+    }
+
+    if (hasColorTransform()) {
+        layer.colorTransform = getColorTransform();
+    }
+
+    const auto roundedCornerState = getRoundedCornerState();
+    layer.geometry.roundedCornersRadius = roundedCornerState.radius;
+    layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+    layer.alpha = alpha;
+    layer.sourceDataspace = mCurrentDataSpace;
+    return true;
 }
 
 void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
@@ -1311,11 +1350,14 @@
     return true;
 }
 
-void Layer::setInfo(int32_t type, int32_t appId) {
-    mCurrentState.appId = appId;
-    mCurrentState.type = type;
+bool Layer::setMetadata(LayerMetadata data) {
+    bool changed = data.mMap != mCurrentState.metadata.mMap;
+    if (!changed) return false;
+    mCurrentState.metadata = std::move(data);
+    mCurrentState.sequence++;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
+    return true;
 }
 
 bool Layer::setLayerStack(uint32_t layerStack) {
@@ -1620,19 +1662,26 @@
         return false;
     }
 
+    sp<Layer> newParent;
+    if (newParentHandle != nullptr) {
+        auto handle = static_cast<Handle*>(newParentHandle.get());
+        newParent = handle->owner.promote();
+        if (newParent == nullptr) {
+            ALOGE("Unable to promote Layer handle");
+            return false;
+        }
+        if (newParent == this) {
+            ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str());
+            return false;
+        }
+    }
+
     sp<Layer> parent = getParent();
     if (parent != nullptr) {
         parent->removeChild(this);
     }
 
     if (newParentHandle != nullptr) {
-        auto handle = static_cast<Handle*>(newParentHandle.get());
-        sp<Layer> newParent = handle->owner.promote();
-        if (newParent == nullptr) {
-            ALOGE("Unable to promote Layer handle");
-            return false;
-        }
-
         newParent->addChild(this);
         if (!newParent->isRemovedFromCurrentState()) {
             addToCurrentState();
@@ -2083,8 +2132,6 @@
 
     layerInfo->set_queued_frames(getQueuedFrameCount());
     layerInfo->set_refresh_pending(isBufferLatched());
-    layerInfo->set_window_type(state.type);
-    layerInfo->set_app_id(state.appId);
     layerInfo->set_curr_frame(mCurrentFrameNumber);
     layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
@@ -2096,6 +2143,11 @@
             barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
         }
     }
+
+    auto protoMap = layerInfo->mutable_metadata();
+    for (const auto& entry : state.metadata.mMap) {
+        (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+    }
 }
 
 void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) {
@@ -2134,6 +2186,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();
@@ -2144,7 +2200,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 afd48ed..00e1a6f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -167,8 +167,7 @@
         Region activeTransparentRegion_legacy;
         Region requestedTransparentRegion_legacy;
 
-        int32_t appId;
-        int32_t type;
+        LayerMetadata metadata;
 
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
@@ -202,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
@@ -284,7 +287,7 @@
                                               uint64_t frameNumber);
     virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
-    virtual void setInfo(int32_t type, int32_t appId);
+    virtual bool setMetadata(LayerMetadata data);
     virtual bool reparentChildren(const sp<IBinder>& layer);
     virtual void setChildrenDrawingParent(const sp<Layer>& layer);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -414,11 +417,9 @@
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
 
 protected:
-    /*
-     * onDraw - draws the surface.
-     */
-    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
-                        bool useIdentityTransform) = 0;
+    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer) = 0;
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
@@ -467,13 +468,15 @@
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
 
-
     /*
-     * draw - performs some global clipping optimizations
-     * and calls onDraw().
+     * prepareClientLayer - populates a renderengine::LayerSettings to passed to
+     * RenderEngine::drawLayers. Returns true if the layer can be used, and
+     * false otherwise.
      */
-    void draw(const RenderArea& renderArea, const Region& clip);
-    void draw(const RenderArea& renderArea, bool useIdentityTransform);
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
+    bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+                            Region& clearRegion, renderengine::LayerSettings& layer);
 
     /*
      * doTransaction - process the transaction. This is a good place to figure
@@ -804,7 +807,13 @@
     FenceTimeline mReleaseTimeline;
 
     // main thread
+    // Active buffer fields
     sp<GraphicBuffer> mActiveBuffer;
+    sp<Fence> mActiveBufferFence;
+    // False if the buffer and its contents have been previously used for GPU
+    // composition, true otherwise.
+    bool mIsActiveBufferUpdatedForGpu = true;
+
     ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
     Rect mCurrentCrop;
     uint32_t mCurrentTransform{0};
@@ -858,7 +867,6 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
-
     /**
      * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
      * The cropped bounds must be transformed back from parent layer space to child layer space by
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 9b2a6fc..075e238 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -757,18 +757,7 @@
     const uint32_t hwcLatency = 0;
 
     // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
-    const nsecs_t nextRefresh = computeNextRefresh(hwcLatency);
-
-    // The DispSync time is already adjusted for the difference between
-    // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
-    // we don't need to factor that in here.  Pad a little to avoid
-    // weird effects if apps might be requesting times right on the edge.
-    nsecs_t extraPadding = 0;
-    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
-        extraPadding = 1000000; // 1ms (6% of 60Hz)
-    }
-
-    return nextRefresh + extraPadding;
+    return computeNextRefresh(hwcLatency);
 }
 
 } // namespace impl
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/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
new file mode 100644
index 0000000..a0a4455
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PhaseOffsets.h"
+
+#include <cutils/properties.h>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+using namespace android::sysprop;
+
+namespace scheduler {
+
+PhaseOffsets::~PhaseOffsets() = default;
+
+namespace impl {
+PhaseOffsets::PhaseOffsets() {
+    int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
+
+    int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.early_phase_offset_ns", value, "-1");
+    const int earlySfOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
+    const int earlyGlSfOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
+    const int earlyAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
+    const int earlyGlAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
+    const int highFpsEarlySfOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
+    const int highFpsEarlyGlSfOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
+    const int highFpsEarlyAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
+    const int highFpsEarlyGlAppOffsetNs = atoi(value);
+
+    // TODO(b/122905996): Define these in device.mk.
+    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "1000000");
+    const int highFpsLateAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "8000000");
+    const int highFpsLateSfOffsetNs = atoi(value);
+
+    mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
+                                                              : sfVsyncPhaseOffsetNs,
+                                        earlyAppOffsetNs != -1 ? earlyAppOffsetNs
+                                                               : vsyncPhaseOffsetNs};
+    mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
+                                                                  : sfVsyncPhaseOffsetNs,
+                                          earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
+                                                                   : vsyncPhaseOffsetNs};
+    mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+
+    mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+                                                                  : highFpsLateAppOffsetNs,
+                                     highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+                                                                   : highFpsLateSfOffsetNs};
+    mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+                                                                      : highFpsLateAppOffsetNs,
+                                       highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+                                                                       : highFpsLateSfOffsetNs};
+    mHighRefreshRateOffsets.late = {highFpsLateAppOffsetNs, highFpsLateSfOffsetNs};
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
+    switch (mRefreshRateType) {
+        case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
+            return mHighRefreshRateOffsets;
+        default:
+            return mDefaultRefreshRateOffsets;
+    }
+}
+
+void PhaseOffsets::dump(std::string& result) const {
+    const auto [early, earlyGl, late] = getCurrentOffsets();
+    base::StringAppendF(&result,
+                        "         app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
+                        "   early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
+                        "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
+                        late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+}
+
+nsecs_t PhaseOffsets::getCurrentAppOffset() {
+    return getCurrentOffsets().late.app;
+}
+
+nsecs_t PhaseOffsets::getCurrentSfOffset() {
+    return getCurrentOffsets().late.sf;
+}
+
+} // namespace impl
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
new file mode 100644
index 0000000..cbcaade
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+
+#include "RefreshRateConfigs.h"
+#include "VSyncModulator.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class encapsulates offsets for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class PhaseOffsets {
+public:
+    struct Offsets {
+        VSyncModulator::Offsets early;
+        VSyncModulator::Offsets earlyGl;
+        VSyncModulator::Offsets late;
+    };
+
+    virtual ~PhaseOffsets();
+
+    virtual nsecs_t getCurrentAppOffset() = 0;
+    virtual nsecs_t getCurrentSfOffset() = 0;
+    virtual Offsets getCurrentOffsets() const = 0;
+    virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
+    virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+class PhaseOffsets : public scheduler::PhaseOffsets {
+public:
+    PhaseOffsets();
+
+    nsecs_t getCurrentAppOffset() override;
+    nsecs_t getCurrentSfOffset() override;
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    Offsets getCurrentOffsets() const override;
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
+        mRefreshRateType = refreshRateType;
+    }
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& result) const override;
+
+private:
+    Offsets getmDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
+    Offsets getmHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
+
+    std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
+            RefreshRateConfigs::RefreshRateType::DEFAULT;
+
+    Offsets mDefaultRefreshRateOffsets;
+    Offsets mHighRefreshRateOffsets;
+};
+} // namespace impl
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2ed2866..00820f1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -70,7 +70,7 @@
     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
 
     char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "0");
+    property_get("debug.sf.set_idle_timer_ms", value, "30");
     mSetIdleTimerMs = atoi(value);
 
     if (mSetIdleTimerMs > 0) {
@@ -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/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index dde0c57..d7ec733 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -18,9 +18,10 @@
 
 #include <utils/Errors.h>
 
+#include <cinttypes>
 #include <mutex>
 
-using namespace android::surfaceflinger;
+#include "Scheduler.h"
 
 namespace android {
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5a866b2..2cf2cd8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <sys/types.h>
@@ -122,6 +122,7 @@
 using base::StringAppendF;
 using ui::ColorMode;
 using ui::Dataspace;
+using ui::DisplayPrimaries;
 using ui::Hdr;
 using ui::RenderIntent;
 
@@ -196,9 +197,20 @@
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
+constexpr float kSrgbRedX = 0.4123f;
+constexpr float kSrgbRedY = 0.2126f;
+constexpr float kSrgbRedZ = 0.0193f;
+constexpr float kSrgbGreenX = 0.3576f;
+constexpr float kSrgbGreenY = 0.7152f;
+constexpr float kSrgbGreenZ = 0.1192f;
+constexpr float kSrgbBlueX = 0.1805f;
+constexpr float kSrgbBlueY = 0.0722f;
+constexpr float kSrgbBlueZ = 0.9506f;
+constexpr float kSrgbWhiteX = 0.9505f;
+constexpr float kSrgbWhiteY = 1.0000f;
+constexpr float kSrgbWhiteZ = 1.0891f;
+
 // ---------------------------------------------------------------------------
-int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
-int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
 uint64_t SurfaceFlinger::maxVirtualDisplaySize;
@@ -259,6 +271,7 @@
         mLayersRemoved(false),
         mLayersAdded(false),
         mBootTime(systemTime()),
+        mPhaseOffsets{getFactory().createPhaseOffsets()},
         mVisibleRegionsDirty(false),
         mGeometryInvalid(false),
         mAnimCompositionPending(false),
@@ -284,10 +297,6 @@
       : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
 
-    vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
-
-    sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
-
     hasSyncFramework = running_without_sync_framework(true);
 
     dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
@@ -340,6 +349,16 @@
             getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
                                         SurfaceFlinger::dispSyncPresentTimeOffset);
 
+    auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (surfaceFlingerConfigsServiceV1_2) {
+        surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries(
+                [&](auto tmpPrimaries) {
+                    memcpy(&mInternalDisplayPrimaries, &tmpPrimaries, sizeof(ui::DisplayPrimaries));
+                });
+    } else {
+        initDefaultDisplayNativePrimaries();
+    }
+
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
@@ -376,29 +395,11 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    property_get("debug.sf.early_phase_offset_ns", value, "-1");
-    const int earlySfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
-    const int earlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
-    const int earlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
-    const int earlyGlAppOffsetNs = atoi(value);
-
     property_get("debug.sf.use_scheduler", value, "0");
     mUseScheduler = atoi(value);
 
-    const VSyncModulator::Offsets earlyOffsets =
-            {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
-            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
-    const VSyncModulator::Offsets earlyGlOffsets =
-            {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
-            earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
-    mVsyncModulator.setPhaseOffsets(earlyOffsets, earlyGlOffsets,
-            {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs});
+    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+    mVsyncModulator.setPhaseOffsets(early, gl, late);
 
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
@@ -605,7 +606,7 @@
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
 
-    ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs);
+    ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
 
     Mutex::Autolock _l(mStateLock);
 
@@ -616,13 +617,17 @@
         mScheduler = getFactory().createScheduler([this](bool enabled) {
             setVsyncEnabled(EventThread::DisplayType::Primary, enabled);
         });
+        // TODO(b/113612090): Currently we assume that if scheduler is turned on, then the refresh
+        // rate is 90. Once b/122905403 is completed, this should be updated accordingly.
+        mPhaseOffsets->setRefreshRateType(
+                scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
 
         mAppConnectionHandle =
-                mScheduler->createConnection("appConnection", SurfaceFlinger::vsyncPhaseOffsetNs,
+                mScheduler->createConnection("appConnection", mPhaseOffsets->getCurrentAppOffset(),
                                              resyncCallback,
                                              impl::EventThread::InterceptVSyncsCallback());
         mSfConnectionHandle =
-                mScheduler->createConnection("sfConnection", SurfaceFlinger::sfVsyncPhaseOffsetNs,
+                mScheduler->createConnection("sfConnection", mPhaseOffsets->getCurrentSfOffset(),
                                              resyncCallback, [this](nsecs_t timestamp) {
                                                  mInterceptor->saveVSyncEvent(timestamp);
                                              });
@@ -633,14 +638,14 @@
     } else {
         mEventThreadSource =
                 std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
-                                                 SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
+                                                 mPhaseOffsets->getCurrentAppOffset(), true, "app");
         mEventThread =
                 std::make_unique<impl::EventThread>(mEventThreadSource.get(),
                                                     impl::EventThread::InterceptVSyncsCallback(),
                                                     "appEventThread");
         mSfEventThreadSource =
                 std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
-                                                 SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+                                                 mPhaseOffsets->getCurrentSfOffset(), true, "sf");
 
         mSFEventThread =
                 std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
@@ -721,8 +726,20 @@
     }
 
     if (mUseScheduler) {
-        mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); });
-        mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); });
+        mScheduler->setExpiredIdleTimerCallback([this]() {
+            mPhaseOffsets->setRefreshRateType(
+                    scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
+            const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+            mVsyncModulator.setPhaseOffsets(early, gl, late);
+            setRefreshRateTo(60.f /* fps */);
+        });
+        mScheduler->setResetIdleTimerCallback([this]() {
+            mPhaseOffsets->setRefreshRateType(
+                    scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
+            const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+            mVsyncModulator.setPhaseOffsets(early, gl, late);
+            setRefreshRateTo(90.f /* fps */);
+        });
         mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(
                 getHwComposer().getConfigs(*display->getId()));
     }
@@ -877,7 +894,7 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        info.appVsyncOffset = vsyncPhaseOffsetNs;
+        info.appVsyncOffset = mPhaseOffsets->getCurrentAppOffset();
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -891,8 +908,8 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline = hwConfig->getVsyncPeriod() -
-                sfVsyncPhaseOffsetNs + 1000000;
+        info.presentationDeadline =
+                hwConfig->getVsyncPeriod() - mPhaseOffsets->getCurrentSfOffset() + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
@@ -1012,6 +1029,21 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                                   ui::DisplayPrimaries &primaries) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    // Currently we only support this API for a single internal display.
+    if (getInternalDisplayToken() != displayToken) {
+        return BAD_VALUE;
+    }
+
+    memcpy(&primaries, &mInternalDisplayPrimaries, sizeof(ui::DisplayPrimaries));
+    return NO_ERROR;
+}
+
 ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
     if (const auto display = getDisplayDevice(displayToken)) {
         return display->getCompositionDisplay()->getState().colorMode;
@@ -1143,6 +1175,20 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken,
+                                            bool* outIsWideColorDisplay) const {
+    if (!displayToken || !outIsWideColorDisplay) {
+        return BAD_VALUE;
+    }
+    Mutex::Autolock _l(mStateLock);
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return BAD_VALUE;
+    }
+    *outIsWideColorDisplay = display->hasWideColorGamut();
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
         Mutex::Autolock _l(mStateLock);
@@ -1823,16 +1869,13 @@
 
     if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
+        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
         if (!dirtyRegion.isEmpty()) {
+            base::unique_fd readyFence;
             // redraw the whole screen
-            doComposeSurfaces(displayDevice);
+            doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
 
-            // and draw the dirty region
-            auto& engine(getRenderEngine());
-            engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
-
-            display->getRenderSurface()->queueBuffer();
+            display->getRenderSurface()->queueBuffer(std::move(readyFence));
         }
     }
 
@@ -1916,11 +1959,11 @@
                                                 nsecs_t compositeToPresentLatency) {
     // Integer division and modulo round toward 0 not -inf, so we need to
     // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0)
-            ? (stats.vsyncPeriod - (sfVsyncPhaseOffsetNs % stats.vsyncPeriod))
-            : ((-sfVsyncPhaseOffsetNs) % stats.vsyncPeriod);
+    nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
+            ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
+            : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
 
-    // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+    // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
     if (idealLatency <= 0) {
         idealLatency = stats.vsyncPeriod;
     }
@@ -1929,7 +1972,7 @@
     // composition and present times, which often have >1ms of jitter.
     // Reducing jitter is important if an app attempts to extrapolate
     // something (such as user input) to an accurate diasplay time.
-    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
     // with (presentLatency % interval).
     nsecs_t bias = stats.vsyncPeriod / 2;
     int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
@@ -2351,7 +2394,7 @@
     auto display = displayDevice->getCompositionDisplay();
     const auto& displayState = display->getState();
 
-    bool dirty = !display->getPhysicalSpaceDirtyRegion(false).isEmpty();
+    bool dirty = !display->getDirtyRegion(false).isEmpty();
     bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
     bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
 
@@ -2402,7 +2445,7 @@
 
     if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
+        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
 
         // repaint the framebuffer (if needed)
         doDisplayComposition(displayDevice, dirtyRegion);
@@ -2550,6 +2593,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,
@@ -2654,19 +2708,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 {
@@ -2772,23 +2816,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);
                         }
                     }
                 }
@@ -3189,6 +3219,21 @@
     }
 }
 
+void SurfaceFlinger::initDefaultDisplayNativePrimaries() {
+    mInternalDisplayPrimaries.red.X = kSrgbRedX;
+    mInternalDisplayPrimaries.red.Y = kSrgbRedY;
+    mInternalDisplayPrimaries.red.Z = kSrgbRedZ;
+    mInternalDisplayPrimaries.green.X = kSrgbGreenX;
+    mInternalDisplayPrimaries.green.Y = kSrgbGreenY;
+    mInternalDisplayPrimaries.green.Z = kSrgbGreenZ;
+    mInternalDisplayPrimaries.blue.X = kSrgbBlueX;
+    mInternalDisplayPrimaries.blue.Y = kSrgbBlueY;
+    mInternalDisplayPrimaries.blue.Z = kSrgbBlueZ;
+    mInternalDisplayPrimaries.white.X = kSrgbWhiteX;
+    mInternalDisplayPrimaries.white.Y = kSrgbWhiteY;
+    mInternalDisplayPrimaries.white.Z = kSrgbWhiteZ;
+}
+
 bool SurfaceFlinger::handlePageFlip()
 {
     ALOGV("handlePageFlip");
@@ -3285,13 +3330,15 @@
     }
 
     ALOGV("doDisplayComposition");
-    if (!doComposeSurfaces(displayDevice)) return;
+    base::unique_fd readyFence;
+    if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
 
     // swap buffers (presentation)
-    display->getRenderSurface()->queueBuffer();
+    display->getRenderSurface()->queueBuffer(std::move(readyFence));
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
+                                       const Region& debugRegion, base::unique_fd* readyFence) {
     ALOGV("doComposeSurfaces");
 
     auto display = displayDevice->getCompositionDisplay();
@@ -3306,13 +3353,14 @@
     mat4 colorMatrix;
     bool applyColorMatrix = false;
 
-    // Framebuffer will live in this scope for GPU composition.
-    std::unique_ptr<renderengine::BindNativeBufferAsFramebuffer> fbo;
+    renderengine::DisplaySettings clientCompositionDisplay;
+    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    sp<GraphicBuffer> buf;
 
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
-        sp<GraphicBuffer> buf = display->getRenderSurface()->dequeueBuffer();
+        buf = display->getRenderSurface()->dequeueBuffer();
 
         if (buf == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
@@ -3321,24 +3369,30 @@
             return false;
         }
 
-        // Bind the framebuffer in this scope.
-        fbo = std::make_unique<renderengine::BindNativeBufferAsFramebuffer>(getRenderEngine(),
-                                                                            buf->getNativeBuffer());
+        clientCompositionDisplay.physicalDisplay = displayState.scissor;
+        clientCompositionDisplay.clip = displayState.scissor;
+        const ui::Transform& displayTransform = displayState.transform;
+        mat4 m;
+        m[0][0] = displayTransform[0][0];
+        m[0][1] = displayTransform[0][1];
+        m[0][3] = displayTransform[0][2];
+        m[1][0] = displayTransform[1][0];
+        m[1][1] = displayTransform[1][1];
+        m[1][3] = displayTransform[1][2];
+        m[3][0] = displayTransform[2][0];
+        m[3][1] = displayTransform[2][1];
+        m[3][3] = displayTransform[2][2];
 
-        if (fbo->getStatus() != NO_ERROR) {
-            ALOGW("Binding buffer for display [%s] failed with status: %d",
-                  displayDevice->getDisplayName().c_str(), fbo->getStatus());
-            return false;
-        }
+        clientCompositionDisplay.globalTransform = m;
 
         const auto* profile = display->getDisplayColorProfile();
         Dataspace outputDataspace = Dataspace::UNKNOWN;
         if (profile->hasWideColorGamut()) {
             outputDataspace = displayState.dataspace;
         }
-        getRenderEngine().setOutputDataSpace(outputDataspace);
-        getRenderEngine().setDisplayMaxLuminance(
-                profile->getHdrCapabilities().getDesiredMaxLuminance());
+        clientCompositionDisplay.outputDataspace = outputDataspace;
+        clientCompositionDisplay.maxLuminance =
+                profile->getHdrCapabilities().getDesiredMaxLuminance();
 
         const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
         const bool skipClientColorTransform =
@@ -3349,44 +3403,7 @@
         // Compute the global color transform matrix.
         applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
         if (applyColorMatrix) {
-            colorMatrix = mDrawingState.colorMatrix;
-        }
-
-        display->getRenderSurface()->setViewportAndProjection();
-
-        // Never touch the framebuffer if we don't have any framebuffer layers
-        if (hasDeviceComposition) {
-            // when using overlays, we assume a fully transparent framebuffer
-            // NOTE: we could reduce how much we need to clear, for instance
-            // remove where there are opaque FB layers. however, on some
-            // GPUs doing a "clean slate" clear might be more efficient.
-            // We'll revisit later if needed.
-            getRenderEngine().clearWithColor(0, 0, 0, 0);
-        } else {
-            // we start with the whole screen area and remove the scissor part
-            // we're left with the letterbox region
-            // (common case is that letterbox ends-up being empty)
-            const Region letterbox = bounds.subtract(displayState.scissor);
-
-            // compute the area to clear
-            const Region region = displayState.undefinedRegion.merge(letterbox);
-
-            // screen is already cleared here
-            if (!region.isEmpty()) {
-                // can happen with SurfaceView
-                drawWormhole(region);
-            }
-        }
-
-        const Rect& bounds = displayState.bounds;
-        const Rect& scissor = displayState.scissor;
-        if (scissor != bounds) {
-            // scissor doesn't match the screen's dimensions, so we
-            // need to clear everything outside of it and enable
-            // the GL scissor so we don't draw anything where we shouldn't
-
-            // enable scissor for this frame
-            getRenderEngine().setScissor(scissor);
+            clientCompositionDisplay.colorTransform = colorMatrix;
         }
     }
 
@@ -3395,11 +3412,11 @@
      */
 
     ALOGV("Rendering client layers");
-    const ui::Transform& displayTransform = displayState.transform;
     bool firstLayer = true;
+    Region clearRegion = Region::INVALID_REGION;
     for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-        const Region clip(bounds.intersect(
-                displayTransform.transform(layer->visibleRegion)));
+        const Region viewportRegion(displayState.viewport);
+        const Region clip(viewportRegion.intersect(layer->visibleRegion));
         ALOGV("Layer: %s", layer->getName().string());
         ALOGV("  Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str());
         if (!clip.isEmpty()) {
@@ -3415,22 +3432,28 @@
                         layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
                         // never clear the very first layer since we're
                         // guaranteed the FB is already cleared
-                        layer->clearWithOpenGL(renderArea);
+                        renderengine::LayerSettings layerSettings;
+                        Region dummyRegion;
+                        bool prepared = layer->prepareClientLayer(renderArea, clip, dummyRegion,
+                                                                  layerSettings);
+
+                        if (prepared) {
+                            layerSettings.source.buffer.buffer = nullptr;
+                            layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+                            layerSettings.alpha = half(0.0);
+                            layerSettings.disableBlending = true;
+                            clientCompositionLayers.push_back(layerSettings);
+                        }
                     }
                     break;
                 }
                 case HWC2::Composition::Client: {
-                    if (layer->hasColorTransform()) {
-                        mat4 tmpMatrix;
-                        if (applyColorMatrix) {
-                            tmpMatrix = mDrawingState.colorMatrix;
-                        }
-                        tmpMatrix *= layer->getColorTransform();
-                        getRenderEngine().setColorTransform(tmpMatrix);
-                    } else {
-                        getRenderEngine().setColorTransform(colorMatrix);
+                    renderengine::LayerSettings layerSettings;
+                    bool prepared =
+                            layer->prepareClientLayer(renderArea, clip, clearRegion, layerSettings);
+                    if (prepared) {
+                        clientCompositionLayers.push_back(layerSettings);
                     }
-                    layer->draw(renderArea, clip);
                     break;
                 }
                 default:
@@ -3442,14 +3465,23 @@
         firstLayer = false;
     }
 
-    // Perform some cleanup steps if we used client composition.
     if (hasClientComposition) {
-        getRenderEngine().setColorTransform(mat4());
-        getRenderEngine().disableScissor();
-        display->getRenderSurface()->finishBuffer();
-        // Clear out error flags here so that we don't wait until next
-        // composition to log.
-        getRenderEngine().checkErrors();
+        clientCompositionDisplay.clearRegion = clearRegion;
+        if (!debugRegion.isEmpty()) {
+            Region::const_iterator it = debugRegion.begin();
+            Region::const_iterator end = debugRegion.end();
+            while (it != end) {
+                const Rect& rect = *it++;
+                renderengine::LayerSettings layerSettings;
+                layerSettings.source.buffer.buffer = nullptr;
+                layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+                layerSettings.geometry.boundaries = rect.toFloatRect();
+                layerSettings.alpha = half(1.0);
+                clientCompositionLayers.push_back(layerSettings);
+            }
+        }
+        getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers,
+                                     buf->getNativeBuffer(), readyFence);
     }
     return true;
 }
@@ -3968,6 +4000,9 @@
             ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
         }
     }
+    if (what & layer_state_t::eMetadataChanged) {
+        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+    }
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
         mTransactionCompletedThread.run();
@@ -3996,13 +4031,10 @@
     return flags;
 }
 
-status_t SurfaceFlinger::createLayer(
-        const String8& name,
-        const sp<Client>& client,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
-        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
-{
+status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
+                                     uint32_t h, PixelFormat format, uint32_t flags,
+                                     LayerMetadata metadata, sp<IBinder>* handle,
+                                     sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -4058,12 +4090,15 @@
 
     // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
     // TODO b/64227542
-    if (windowType == 441731) {
-        windowType = 2024; // TYPE_NAVIGATION_BAR_PANEL
-        layer->setPrimaryDisplayOnly();
+    if (metadata.has(METADATA_WINDOW_TYPE)) {
+        int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+        if (windowType == 441731) {
+            metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL
+            layer->setPrimaryDisplayOnly();
+        }
     }
 
-    layer->setInfo(windowType, ownerUid);
+    layer->setMetadata(std::move(metadata));
 
     bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
     result = addClientLayer(client, *handle, *gbp, layer, *parent,
@@ -4478,15 +4513,10 @@
 }
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
-    const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
-    const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
+    mPhaseOffsets->dump(result);
     StringAppendF(&result,
-                  "         app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
-                  "   early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
-                  "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
                   "    present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
-                  vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset,
-                  appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod());
+                  dispSyncPresentTimeOffset, getVsyncPeriod());
 
     StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled");
 
@@ -4971,6 +5001,7 @@
         case GET_ACTIVE_CONFIG:
         case GET_BUILT_IN_DISPLAY:
         case GET_DISPLAY_COLOR_MODES:
+        case GET_DISPLAY_NATIVE_PRIMARIES:
         case GET_DISPLAY_CONFIGS:
         case GET_DISPLAY_STATS:
         case GET_SUPPORTED_FRAME_TIMESTAMPS:
@@ -4982,7 +5013,8 @@
         case GET_COMPOSITION_PREFERENCE:
         case GET_PROTECTED_CONTENT_SUPPORT:
         case CACHE_BUFFER:
-        case UNCACHE_BUFFER: {
+        case UNCACHE_BUFFER:
+        case IS_WIDE_COLOR_DISPLAY: {
             return OK;
         }
         case CAPTURE_LAYERS:
@@ -5580,35 +5612,109 @@
 
 void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
                                             TraverseLayersFunction traverseLayers,
-                                            bool useIdentityTransform) {
+                                            ANativeWindowBuffer* buffer, bool useIdentityTransform,
+                                            int* outSyncFd) {
     ATRACE_CALL();
 
-    auto& engine(getRenderEngine());
-
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
     const auto sourceCrop = renderArea.getSourceCrop();
     const auto rotation = renderArea.getRotationFlags();
 
-    engine.setOutputDataSpace(renderArea.getReqDataSpace());
-    engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
+    renderengine::DisplaySettings clientCompositionDisplay;
+    std::vector<renderengine::LayerSettings> clientCompositionLayers;
 
-    // make sure to clear all GL error flags
-    engine.checkErrors();
+    // assume that bounds are never offset, and that they are the same as the
+    // buffer bounds.
+    clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
+    ui::Transform transform = renderArea.getTransform();
+    mat4 m;
+    m[0][0] = transform[0][0];
+    m[0][1] = transform[0][1];
+    m[0][3] = transform[0][2];
+    m[1][0] = transform[1][0];
+    m[1][1] = transform[1][1];
+    m[1][3] = transform[1][2];
+    m[3][0] = transform[2][0];
+    m[3][1] = transform[2][1];
+    m[3][3] = transform[2][2];
 
-    // set-up our viewport
-    engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, rotation);
-    engine.disableTexturing();
+    clientCompositionDisplay.globalTransform = m;
+    mat4 rotMatrix;
+    // Displacement for repositioning the clipping rectangle after rotating it
+    // with the rotation hint.
+    int displacementX = 0;
+    int displacementY = 0;
+    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+    switch (rotation) {
+        case ui::Transform::ROT_90:
+            rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+            displacementX = reqWidth;
+            break;
+        case ui::Transform::ROT_180:
+            rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+            displacementX = reqWidth;
+            displacementY = reqHeight;
+            break;
+        case ui::Transform::ROT_270:
+            rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+            displacementY = reqHeight;
+            break;
+        default:
+            break;
+    }
+    // We need to transform the clipping window into the right spot.
+    // First, rotate the clipping rectangle by the rotation hint to get the
+    // right orientation
+    const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
+    const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
+    const vec4 rotClipTL = rotMatrix * clipTL;
+    const vec4 rotClipBR = rotMatrix * clipBR;
+    const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
+    const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
+    const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
+    const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
+
+    // Now reposition the clipping rectangle with the displacement vector
+    // computed above.
+    const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
+
+    clientCompositionDisplay.clip =
+            Rect(newClipLeft + displacementX, newClipTop + displacementY,
+                 newClipRight + displacementX, newClipBottom + displacementY);
+
+    // We need to perform the same transformation in layer space, so propagate
+    // it to the global transform.
+    mat4 clipTransform = displacementMat * rotMatrix;
+    clientCompositionDisplay.globalTransform *= clipTransform;
+    clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
+    clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
-    // redraw the screen entirely...
-    engine.clearWithColor(0, 0, 0, alpha);
 
+    renderengine::LayerSettings fillLayer;
+    fillLayer.source.buffer.buffer = nullptr;
+    fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
+    fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+    fillLayer.alpha = half(alpha);
+    clientCompositionLayers.push_back(fillLayer);
+
+    Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
-        engine.setColorTransform(layer->getColorTransform());
-        layer->draw(renderArea, useIdentityTransform);
-        engine.setColorTransform(mat4());
+        renderengine::LayerSettings layerSettings;
+        bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
+                                                  layerSettings);
+        if (prepared) {
+            clientCompositionLayers.push_back(layerSettings);
+        }
     });
+
+    clientCompositionDisplay.clearRegion = clearRegion;
+    base::unique_fd drawFence;
+    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+                                 &drawFence);
+
+    *outSyncFd = drawFence.release();
 }
 
 status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5632,28 +5738,7 @@
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
-    auto& engine(getRenderEngine());
-
-    // this binds the given EGLImage as a framebuffer for the
-    // duration of this scope.
-    renderengine::BindNativeBufferAsFramebuffer bufferBond(engine, buffer);
-    if (bufferBond.getStatus() != NO_ERROR) {
-        ALOGE("got ANWB binding error while taking screenshot");
-        return INVALID_OPERATION;
-    }
-
-    // this will in fact render into our dequeued buffer
-    // via an FBO, which means we didn't have to create
-    // an EGLSurface and therefore we're not
-    // dependent on the context's EGLConfig.
-    renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform);
-
-    base::unique_fd syncFd = engine.flush();
-    if (syncFd < 0) {
-        engine.finish();
-    }
-    *outSyncFd = syncFd.release();
-
+    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 68a602c..a48e811 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -59,6 +59,7 @@
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncModulator.h"
@@ -141,7 +142,6 @@
     ENHANCED = 2,
 };
 
-
 class SurfaceFlingerBE
 {
 public:
@@ -295,10 +295,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);
 
@@ -443,6 +439,8 @@
     int getActiveConfig(const sp<IBinder>& displayToken) override;
     status_t getDisplayColorModes(const sp<IBinder>& displayToken,
                                   Vector<ui::ColorMode>* configs) override;
+    status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                       ui::DisplayPrimaries &primaries);
     ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
@@ -472,6 +470,8 @@
     status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
                          int32_t* outBufferId) override;
     status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) override;
+    status_t isWideColorDisplay(const sp<IBinder>& displayToken,
+                                bool* outIsWideColorDisplay) const override;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -556,10 +556,9 @@
     /* ------------------------------------------------------------------------
      * Layer management
      */
-    status_t createLayer(const String8& name, const sp<Client>& client,
-            uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-            int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
+    status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
+                         PixelFormat format, uint32_t flags, LayerMetadata metadata,
+                         sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
 
     status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
                                     uint32_t h, uint32_t flags, PixelFormat& format,
@@ -601,7 +600,8 @@
     void startBootAnim();
 
     void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                bool useIdentityTransform);
+                                ANativeWindowBuffer* buffer, bool useIdentityTransform,
+                                int* outSyncFd);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                  sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
                                  bool useIdentityTransform);
@@ -667,6 +667,10 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
+    // Initialize structures containing information about the internal
+    // display's native color coordinates using default data
+    void initDefaultDisplayNativePrimaries();
+
     /* ------------------------------------------------------------------------
      * H/W composer
      */
@@ -736,8 +740,11 @@
     void logLayerStats();
     void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
 
-    // This fails if using GL and the surface has been destroyed.
-    bool doComposeSurfaces(const sp<DisplayDevice>& display);
+    // This fails if using GL and the surface has been destroyed. readyFence
+    // will be populated if using GL and native fence sync is supported, to
+    // signal when drawing has completed.
+    bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
+                           base::unique_fd* readyFence);
 
     void postFramebuffer(const sp<DisplayDevice>& display);
     void postFrame();
@@ -754,6 +761,8 @@
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
+    void dispatchDisplayHotplugEvent(EventThread::DisplayType displayType, bool connected);
+
     /* ------------------------------------------------------------------------
      * VSync
      */
@@ -944,7 +953,10 @@
     std::unique_ptr<EventControlThread> mEventControlThread;
     std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
 
+    // Calculates correct offsets.
     VSyncModulator mVsyncModulator;
+    // Keeps track of all available phase offsets for different refresh types.
+    std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -1085,6 +1097,8 @@
     InputWindowCommands mInputWindowCommands;
 
     BufferStateLayerCache mBufferStateLayerCache;
+
+    ui::DisplayPrimaries mInternalDisplayPrimaries;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 77679e3..773a5d1 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -33,6 +33,7 @@
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
 #include "TimeStats/TimeStats.h"
 
@@ -68,6 +69,10 @@
             return std::make_unique<android::impl::MessageQueue>();
         }
 
+        std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+            return std::make_unique<scheduler::impl::PhaseOffsets>();
+        }
+
         std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override {
             return std::make_unique<Scheduler>(callback);
         }
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index f747684..ac99e2a 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -52,6 +52,10 @@
 namespace compositionengine {
 class CompositionEngine;
 } // namespace compositionengine
+
+namespace scheduler {
+class PhaseOffsets;
+} // namespace scheduler
 namespace surfaceflinger {
 
 class NativeWindowSurface;
@@ -66,6 +70,7 @@
             std::function<void(bool)> setVSyncEnabled) = 0;
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
+    virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
     virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0;
     virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
 
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 43eebd7..b654ba7 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -15,8 +15,8 @@
 namespace sysprop {
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
-using ::android::hardware::graphics::common::V1_1::PixelFormat;
 using ::android::hardware::graphics::common::V1_2::Dataspace;
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
 
 int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
     auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 92ce5c2..9b26883 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -46,13 +46,13 @@
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
 int32_t default_composition_pixel_format(
-        android::hardware::graphics::common::V1_1::PixelFormat defaultValue);
+        android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
 
 int64_t wcg_composition_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
 int32_t wcg_composition_pixel_format(
-        android::hardware::graphics::common::V1_1::PixelFormat defaultValue);
+        android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index ac147fe..cb368b0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,6 +1,5 @@
 cc_library_shared {
     name: "liblayers_proto",
-    vendor_available: true,
     export_include_dirs: ["include"],
 
     srcs: [
@@ -11,6 +10,7 @@
 
     shared_libs: [
         "android.hardware.graphics.common@1.1",
+        "libgui",
         "libui",
         "libprotobuf-cpp-lite",
         "libbase",
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index d020a39..5c72fea 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -117,11 +117,15 @@
     layer.hwcFrame = generateRect(layerProto.hwc_frame());
     layer.hwcCrop = generateFloatRect(layerProto.hwc_crop());
     layer.hwcTransform = layerProto.hwc_transform();
-    layer.windowType = layerProto.window_type();
-    layer.appId = layerProto.app_id();
     layer.hwcCompositionType = layerProto.hwc_composition_type();
     layer.isProtected = layerProto.is_protected();
     layer.cornerRadius = layerProto.corner_radius();
+    for (const auto& entry : layerProto.metadata()) {
+        const std::string& dataStr = entry.second;
+        std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
+        outData.resize(dataStr.size());
+        memcpy(outData.data(), dataStr.data(), dataStr.size());
+    }
 
     return layer;
 }
@@ -310,7 +314,14 @@
     StringAppendF(&result, "      activeBuffer=%s,", activeBuffer.to_string().c_str());
     StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
     StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending);
-    StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId);
+    StringAppendF(&result, " metadata={");
+    bool first = true;
+    for (const auto& entry : metadata.mMap) {
+        if (!first) result.append(", ");
+        first = false;
+        result.append(metadata.itemToString(entry.first, ":"));
+    }
+    result.append("}");
 
     return result;
 }
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index a794ca5..d1b2b1f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -17,6 +17,7 @@
 
 #include <layerproto/LayerProtoHeader.h>
 
+#include <gui/LayerMetadata.h>
 #include <math/vec4.h>
 
 #include <memory>
@@ -110,11 +111,10 @@
         LayerProtoParser::Rect hwcFrame;
         LayerProtoParser::FloatRect hwcCrop;
         int32_t hwcTransform;
-        int32_t windowType;
-        int32_t appId;
         int32_t hwcCompositionType;
         bool isProtected;
         float cornerRadius;
+        LayerMetadata metadata;
 
         std::string to_string() const;
     };
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index b100438..4c756d9 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -74,8 +74,8 @@
   optional FloatRectProto hwc_crop = 31;
   // The layer's composer backend transform
   optional int32 hwc_transform = 32;
-  optional int32 window_type = 33;
-  optional int32 app_id = 34;
+  optional int32 window_type = 33 [deprecated=true];
+  optional int32 app_id = 34 [deprecated=true];
   // The layer's composition type
   optional int32 hwc_composition_type = 35;
   // If it's a buffer layer, indicate if the content is protected
@@ -89,6 +89,8 @@
   optional int32 effective_scaling_mode = 40;
   // Layer's corner radius.
   optional float corner_radius = 41;
+  // Metadata map. May be empty.
+  map<int32, bytes> metadata = 42;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 8560f5e..f021d3d 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -19,6 +19,7 @@
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
+using ui::ColorMode;
 
 namespace {
 const String8 DISPLAY_NAME("Credentials Display Test");
@@ -206,6 +207,15 @@
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
 }
 
+TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        ui::DisplayPrimaries primaries;
+        return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
 TEST_F(CredentialsTest, SetActiveConfigTest) {
     sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
     std::function<status_t()> condition = [=]() {
@@ -312,4 +322,36 @@
     seteuid(AID_BIN);
     ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
 }
+
+TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    bool result = false;
+    status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
+    ASSERT_EQ(NO_ERROR, error);
+    bool hasWideColorMode = false;
+    Vector<ColorMode> colorModes;
+    SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
+    for (ColorMode colorMode : colorModes) {
+        switch (colorMode) {
+            case ColorMode::DISPLAY_P3:
+            case ColorMode::ADOBE_RGB:
+            case ColorMode::DCI_P3:
+                hasWideColorMode = true;
+                break;
+            default:
+                break;
+        }
+    }
+    ASSERT_EQ(hasWideColorMode, result);
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        bool result = false;
+        return SurfaceComposerClient::isWideColorDisplay(display, &result);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index b95c0ec..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);
 }
@@ -2460,6 +2794,23 @@
     ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
 }
 
+TEST_F(LayerTransactionTest, ReparentToSelf) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+    Transaction().reparent(layer, layer->getHandle()).apply();
+
+    {
+        // We expect the transaction to be silently dropped, but for SurfaceFlinger
+        // to still be functioning.
+        SCOPED_TRACE("after reparent to self");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
 class ColorTransformHelper {
 public:
     static void DegammaColorSingle(half& s) {
@@ -2501,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 */,
@@ -2512,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);
@@ -2544,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 */,
@@ -2565,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);
@@ -2599,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 */,
@@ -2620,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);
@@ -2660,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/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index e34e568..b2bcb45 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,6 +42,7 @@
         "EventThreadTest.cpp",
         "IdleTimerTest.cpp",
         "LayerHistoryTest.cpp",
+        "LayerMetadataTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
         "RefreshRateStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e972785..1c4a661 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,8 +45,10 @@
 
 using testing::_;
 using testing::AtLeast;
+using testing::Between;
 using testing::ByMove;
 using testing::DoAll;
+using testing::Field;
 using testing::Invoke;
 using testing::IsNull;
 using testing::Mock;
@@ -158,7 +160,6 @@
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::DispSync* mPrimaryDispSync = new mock::DispSync();
-    renderengine::mock::Image* mReImage = new renderengine::mock::Image();
     renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -270,11 +271,10 @@
         EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
-        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
-
+        // TODO: remove once we verify that we can just grab the fence from the
+        // FramebufferSurface.
         EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
-            return base::unique_fd(0);
+            return base::unique_fd();
         }));
 
         EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
@@ -286,36 +286,18 @@
 
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
-        // Called once with a non-null value to set a framebuffer, and then
-        // again with nullptr to clear it.
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
-                .WillOnce(Return(true));
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
-                .WillOnce(Return(true));
-
-        EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
-        EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
-                .WillOnce(Return(
-                        ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
-        EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, clearWithColor(0, 0, 0, 1)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd())));
-        EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true));
-
-        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
-                .Times(1);
-        // This expectation retires on saturation as setViewportAndProjection is
-        // called an extra time for the code path this setup is for.
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mRenderEngine,
-                    setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
-                                             Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                             ui::Transform::ROT_0))
-                .Times(1)
-                .RetiresOnSaturation();
-        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillRepeatedly(
+                        [](const renderengine::DisplaySettings& displaySettings,
+                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.physicalDisplay);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.clip);
+                            return NO_ERROR;
+                        });
     }
 
     static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -339,31 +321,23 @@
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
 
-        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
-                .Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setColorTransform(_)).Times(2);
-        // These expectations retire on saturation as the code path these
-        // expectations are for appears to make an extra call to them.
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mRenderEngine,
-                    setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
-                                             Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                             ui::Transform::ROT_0))
-                .Times(1);
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
-                .WillOnce(Return(true));
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
-                .WillOnce(Return(true));
-        EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
-                .WillOnce(Return(
-                        ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
-        EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
         EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillRepeatedly(
+                        [](const renderengine::DisplaySettings& displaySettings,
+                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.physicalDisplay);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.clip);
+                            EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+                            return NO_ERROR;
+                        });
     }
 
     template <typename Case>
@@ -374,8 +348,6 @@
     template <typename Case>
     static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupREScreenshotCompositionCallExpectations(test);
-
-        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
     }
 };
 
@@ -387,16 +359,11 @@
     template <typename Case>
     static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupInsecureRECompositionCallExpectations(test);
-
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
     }
 
     template <typename Case>
     static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test);
-
-        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
     }
 };
 
@@ -503,7 +470,6 @@
         bool ignoredRecomputeVisibleRegions;
         layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE);
         Mock::VerifyAndClear(test->mRenderEngine);
-        Mock::VerifyAndClear(test->mReImage);
     }
 
     static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
@@ -586,33 +552,35 @@
     }
 
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine,
-                    setupLayerBlending(true, false, false,
-                                       half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
-                                       0.0f))
-                .Times(1);
-
-        EXPECT_CALL(*test->mRenderEngine, createImage())
-                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
-        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
-        EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
-        // This call retires on saturation as the code that renders a texture disables the state,
-        // along with a top-level disable to ensure it is disabled for non-buffer layers.
-        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so gtet the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
+                    EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
+                    EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE,
+                              layer.source.buffer.cacheHint);
+                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
+                    EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
+                    EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+                    EXPECT_EQ(false, layer.source.buffer.isOpaque);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return NO_ERROR;
+                });
     }
 
     static void setupREBufferCompositionCallExpectations(CompositionTest* test) {
         LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
-
-        // TODO - Investigate and eliminate these differences between display
-        // composition and screenshot composition.
-        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
     }
 
     static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -627,20 +595,28 @@
         LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
     }
 
-    static void setupREColorCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
-    }
-
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine,
-                    setupLayerBlending(true, false, true,
-                                       half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
-                                       0.0f))
-                .Times(1);
-        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so get the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                                    LayerProperties::COLOR[2]),
+                              layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return NO_ERROR;
+                });
     }
 
     static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) {
@@ -680,10 +656,7 @@
         EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
     }
 
-    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, setupFillWithColor(0, 0, 0, 1)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
-    }
+    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
 };
 
 struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
@@ -692,24 +665,25 @@
     static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, createImage())
-                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
-        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
-        EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1);
-
-        EXPECT_CALL(*test->mRenderEngine,
-                    setupLayerBlending(true, false, false,
-                                       half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2],
-                                             Base::COLOR[3]), 0.0f))
-                .Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
-        EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
-        // This call retires on saturation as the code that renders a texture disables the state,
-        // along with a top-level disable to ensure it is disabled for non-buffer layers.
-        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so get the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(1.0f, layer.alpha);
+                    return NO_ERROR;
+                });
     }
 
     static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -813,7 +787,6 @@
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
-        LayerProperties::setupREColorCompositionCommonCallExpectations(test);
         LayerProperties::setupREColorCompositionCallExpectations(test);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 8201704..4cb79ab 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(),
@@ -1219,6 +1226,116 @@
 }
 
 /* ------------------------------------------------------------------------
+ * SurfaceFlinger::getDisplayNativePrimaries
+ */
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+    GetDisplayNativePrimaries();
+    void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+    void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+    static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+    SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+    injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+        ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    primaries.red.X = startingVal++;
+    primaries.red.Y = startingVal++;
+    primaries.red.Z = startingVal++;
+    primaries.green.X = startingVal++;
+    primaries.green.Y = startingVal++;
+    primaries.green.Z = startingVal++;
+    primaries.blue.X = startingVal++;
+    primaries.blue.Y = startingVal++;
+    primaries.blue.Z = startingVal++;
+    primaries.white.X = startingVal++;
+    primaries.white.Y = startingVal++;
+    primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+        const ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    EXPECT_EQ(primaries.red.X, startingVal++);
+    EXPECT_EQ(primaries.red.Y, startingVal++);
+    EXPECT_EQ(primaries.red.Z, startingVal++);
+    EXPECT_EQ(primaries.green.X, startingVal++);
+    EXPECT_EQ(primaries.green.Y, startingVal++);
+    EXPECT_EQ(primaries.green.Z, startingVal++);
+    EXPECT_EQ(primaries.blue.X, startingVal++);
+    EXPECT_EQ(primaries.blue.Y, startingVal++);
+    EXPECT_EQ(primaries.blue.Z, startingVal++);
+    EXPECT_EQ(primaries.white.X, startingVal++);
+    EXPECT_EQ(primaries.white.Y, startingVal++);
+    EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithDefaultPrimariesData) {
+    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+    injector.inject();
+    auto internalDisplayToken = injector.token();
+    // A nullptr would trigger a different execution path than what's being tested here
+    EXPECT_NE(nullptr, internalDisplayToken.get());
+
+    mFlinger.initDefaultDisplayNativePrimaries();
+
+    ui::DisplayPrimaries primaries;
+    // Expecting sRGB primaries
+    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+    EXPECT_EQ(primaries.red.X, 0.4123f);
+    EXPECT_EQ(primaries.red.Y, 0.2126f);
+    EXPECT_EQ(primaries.red.Z, 0.0193f);
+    EXPECT_EQ(primaries.green.X, 0.3576f);
+    EXPECT_EQ(primaries.green.Y, 0.7152f);
+    EXPECT_EQ(primaries.green.Z, 0.1192f);
+    EXPECT_EQ(primaries.blue.X, 0.1805f);
+    EXPECT_EQ(primaries.blue.Y, 0.0722f);
+    EXPECT_EQ(primaries.blue.Z, 0.9506f);
+    EXPECT_EQ(primaries.white.X, 0.9505f);
+    EXPECT_EQ(primaries.white.Y, 1.0000f);
+    EXPECT_EQ(primaries.white.Z, 1.0891f);
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+    injector.inject();
+    auto internalDisplayToken = injector.token();
+
+    ui::DisplayPrimaries expectedPrimaries;
+    populateDummyDisplayNativePrimaries(expectedPrimaries);
+    mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+    sp<BBinder> notInternalDisplayToken = new BBinder();
+
+    ui::DisplayPrimaries primaries;
+    populateDummyDisplayNativePrimaries(primaries);
+    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+    // Check primaries argument wasn't modified in case of failure
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+/* ------------------------------------------------------------------------
  * SurfaceFlinger::setupNewDisplayDeviceInternal
  */
 
@@ -1404,23 +1521,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/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
new file mode 100644
index 0000000..0739f15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/PhaseOffsets.h"
+
+namespace android {
+namespace scheduler {
+
+class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
+    nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+
+public:
+    FakePhaseOffsets() = default;
+    ~FakePhaseOffsets() = default;
+
+    nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
+    nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    PhaseOffsets::Offsets getCurrentOffsets() const override {
+        return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                       {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                       {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+    }
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& /*result*/) const override {}
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
new file mode 100644
index 0000000..92c9f92
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <binder/Parcel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+class LayerMetadataTest : public testing::Test {
+public:
+    LayerMetadataTest();
+    ~LayerMetadataTest() override;
+};
+
+LayerMetadataTest::LayerMetadataTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+LayerMetadataTest::~LayerMetadataTest() {
+    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());
+}
+
+TEST_F(LayerMetadataTest, testLayerMetadata) {
+    LayerMetadata metadata;
+
+    ASSERT_EQ(0, metadata.mMap.size());
+
+    // Test non-set
+    ASSERT_EQ(3, metadata.getInt32(4, 3));
+
+    // Make sure it's still unset
+    ASSERT_EQ(5, metadata.getInt32(4, 5));
+
+    metadata.setInt32(4, 2);
+    ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+    // data is too small
+    metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'};
+    ASSERT_EQ(0, metadata.getInt32(2, 0));
+
+    LayerMetadata second;
+    std::vector<uint8_t> someData{'c', 'd', '\0'};
+    second.mMap[2] = someData;
+    second.setInt32(6, 5);
+    metadata.merge(second);
+
+    ASSERT_EQ(3, metadata.mMap.size());
+    ASSERT_EQ(someData, second.mMap[2]);
+    ASSERT_EQ(5, metadata.getInt32(6, 0));
+    ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+    Parcel p;
+    metadata.writeToParcel(&p);
+    LayerMetadata reconstructed;
+    reconstructed.setInt32(3, 1); // to make sure it gets replaced
+    p.setDataPosition(0);
+    reconstructed.readFromParcel(&p);
+    ASSERT_EQ(metadata.mMap, reconstructed.mMap);
+}
+
+} // namespace
+} // namespace android
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..3a62c40 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -23,6 +23,7 @@
 #include "ColorLayer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "FakePhaseOffsets.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "StartPropertySetThread.h"
@@ -75,6 +76,10 @@
         return std::make_unique<android::impl::MessageQueue>();
     }
 
+    std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+        return std::make_unique<scheduler::FakePhaseOffsets>();
+    }
+
     std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
         // TODO: Use test-fixture controlled factory
         return nullptr;
@@ -187,6 +192,10 @@
         mFactory.mCreateNativeWindowSurface = f;
     }
 
+    void setInternalDisplayPrimaries(const ui::DisplayPrimaries& primaries) {
+        memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
+    }
+
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
     auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
@@ -260,6 +269,15 @@
         return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
     }
 
+    auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                   ui::DisplayPrimaries &primaries) {
+        return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
+    }
+
+    void initDefaultDisplayNativePrimaries() {
+        mFlinger->SurfaceFlinger::initDefaultDisplayNativePrimaries();
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -290,6 +308,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 +336,7 @@
         mutableEventControlThread().reset();
         mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableSFEventThread().reset();
         mutableInterceptor().reset();
         mutablePrimaryDispSync().reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 551fae7..e6f1a06 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -29,11 +29,11 @@
 
 using android::hardware::graphics::common::V1_0::ColorTransform;
 using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
 using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
 
 using android::hardware::graphics::composer::V2_1::Config;
 using android::hardware::graphics::composer::V2_1::Display;
diff --git a/services/utils/Android.bp b/services/utils/Android.bp
index 6132956..f3d2bc9 100644
--- a/services/utils/Android.bp
+++ b/services/utils/Android.bp
@@ -18,6 +18,8 @@
 cc_library_static {
     name: "libserviceutils",
 
+    vendor_available: true,
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -27,8 +29,13 @@
         "PriorityDumper.cpp",
     ],
 
-    clang: true,
+    header_libs: [
+        "libutils_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libutils_headers",
+    ],
+
     export_include_dirs: ["include"],
 }
-
-subdirs = ["tests"]
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;